~grzegorz-og.pl/openobject-addons/extra-5.0_account_asset

« back to all changes in this revision

Viewing changes to c2c_timesheet_reports/reminder.py

  • Committer: nicolas.bessi at camptocamp
  • Date: 2009-07-10 13:34:11 UTC
  • Revision ID: nicolas.bessi@camptocamp.com-20090710133411-mm1hd38lwe0wxcol
adding c2c_modules in stable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
# Copyright (c) Camptocamp SA
 
5
# Author: Arnaud Wüst
 
6
#
 
7
#
 
8
#    This file is part of the c2c_timesheet_report module
 
9
#
 
10
#
 
11
# WARNING: This program as such is intended to be used by professional
 
12
# programmers who take the whole responsability of assessing all potential
 
13
# consequences resulting from its eventual inadequacies and bugs
 
14
# End users who are looking for a ready-to-use solution with commercial
 
15
# garantees and support are strongly adviced to contract a Free Software
 
16
# Service Company
 
17
#
 
18
# This program is Free Software; you can redistribute it and/or
 
19
# modify it under the terms of the GNU General Public License
 
20
# as published by the Free Software Foundation; either version 2
 
21
# of the License, or (at your option) any later version.
 
22
#
 
23
# This program is distributed in the hope that it will be useful,
 
24
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
25
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
26
# GNU General Public License for more details.
 
27
#
 
28
# You should have received a copy of the GNU General Public License
 
29
# along with this program; if not, write to the Free Software
 
30
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
31
#
 
32
##############################################################################
 
33
 
 
34
from c2c_reporting_tools.translation import _
 
35
 
 
36
from osv import fields, osv
 
37
import time
 
38
from datetime import datetime, timedelta
 
39
from mx import DateTime
 
40
import pooler
 
41
import tools
 
42
 
 
43
 
 
44
class reminder (osv.osv):
 
45
    _name = "c2c_timesheet_reports.reminder"
 
46
    _description = "Handle the scheduling of messages"
 
47
    
 
48
    
 
49
    _columns  = {
 
50
            'reply_to' : fields.char('Reply To', size=100),
 
51
            'message'  : fields.text('Message'),
 
52
            'subject'  : fields.char('Subject', size=200),
 
53
    }
 
54
        
 
55
      
 
56
    #default cron (the one created if missing)
 
57
    cron = {'active'          : False,
 
58
            'priority'        : 1,
 
59
            'interval_number' : 1,
 
60
            'interval_type'   : 'weeks',
 
61
            'nextcall'        : time.strftime("%Y-%m-%d %H:%M:%S", (datetime.today() + timedelta(days=1)).timetuple() ), #tomorrow same time
 
62
            'numbercall'      : -1,
 
63
            'doall'           : True,
 
64
            'model'           : 'c2c_timesheet_reports.reminder',
 
65
            'function'        : 'run',
 
66
            'args'            : '()',
 
67
            }
 
68
    
 
69
    #default message (the one created if missing)
 
70
    message = {'reply_to': 'spam@camptocamp.com'
 
71
              }
 
72
        
 
73
    
 
74
    
 
75
    def run(self, cr, uid):
 
76
        """ find the reminder recipients and send them an email """
 
77
        
 
78
        #get all companies
 
79
        companies = self.pool.get('res.company').browse(cr, uid, self.pool.get('res.company').search(cr, uid, []))
 
80
        
 
81
        #for each company, get all recipients
 
82
        recipients = []
 
83
        for c in companies: 
 
84
            recipients += self.get_recipients(cr, uid, {}, c) 
 
85
        
 
86
        #get the message to send
 
87
        message_id = self.get_message_id(cr, uid, {})
 
88
        message_data = self.browse(cr, uid, message_id)
 
89
        
 
90
        
 
91
        #send them email if they have an email defined
 
92
        emails = []
 
93
        for r in recipients:
 
94
 
 
95
            if r.work_email:
 
96
                emails.append(r.work_email)
 
97
        if emails :
 
98
            tools.email_send(message_data.reply_to, [], message_data.subject, message_data.message, email_bcc=emails)
 
99
            
 
100
            
 
101
        
 
102
                
 
103
    def get_recipients(self, cr, uid, context, company):
 
104
        """return the list of users that must recieve the email """
 
105
        
 
106
        #get the whole list of employees
 
107
        employees = self.compute_employees_list(cr, uid, context, company)
 
108
        
 
109
        #periods
 
110
        periods = self.compute_periods(company, time.gmtime(), 13)
 
111
        #remove the first one because it's the current one
 
112
        del periods[0]
 
113
        
 
114
 
 
115
        recipients = []
 
116
 
 
117
        # for each employee
 
118
        for e in employees:
 
119
            
 
120
 
 
121
            #if the user still in the company? 
 
122
            in_company = True
 
123
            if (e.started != False and e.started > time.strftime('%Y-%m-%d') ) or (e.ended != False and e.ended < time.strftime('%Y-%m-%d') ):
 
124
                #do nothing... this user is not concerned anymore by timesheets
 
125
                pass
 
126
            else:
 
127
                #and for each periods
 
128
                for p_index in range(len(periods)):
 
129
                    p = periods[p_index]
 
130
                    status = self.compute_timesheet_status(cr, uid, context, company, e, p)
 
131
                    
 
132
                    # if there is a missing sheet or a draft sheet
 
133
                    # and the user can receive alerts
 
134
                    #then  we must alert the user
 
135
                    if status in ['Missing', 'Draft'] and e.receive_timesheet_alerts :
 
136
                        recipients.append(e)
 
137
                        break # no need to go further for this user, he is now added in the list, go to the next one
 
138
                
 
139
        return recipients
 
140
                
 
141
               
 
142
                
 
143
        
 
144
    def compute_periods(self, company, date, periods_number=5):
 
145
        """ return the timeranges to display. This is the 5 last timesheets (depending on the timerange defined for the company) """
 
146
        
 
147
        periods = []
 
148
        
 
149
        (last_start_date, last_end_date) = self.get_last_period_dates( company, date)
 
150
        
 
151
        for cpt in range(periods_number):
 
152
            #find the delta between last_XXX_date to XXX_date
 
153
            if company.timesheet_range == 'month':
 
154
                delta = DateTime.RelativeDateTime(months=-cpt)
 
155
            elif company.timesheet_range == 'week':
 
156
                delta = DateTime.RelativeDateTime(weeks=-cpt)
 
157
            elif company.timesheet_range == 'year':
 
158
                delta = DateTime.RelativeDateTime(years=-cpt)
 
159
            
 
160
            start_date = last_start_date + delta
 
161
            end_date  = last_end_date   + delta
 
162
            periods.append( (start_date, end_date) )
 
163
            
 
164
        return periods
 
165
                    
 
166
 
 
167
    def get_last_period_dates(self, company, date):
 
168
        """ return the start date and end date of the last period to display """
 
169
        
 
170
        # return the first day and last day of the month 
 
171
        if company.timesheet_range == 'month':
 
172
            start_date = DateTime.Date(date.tm_year, date.tm_mon, 1)
 
173
            end_date = start_date + DateTime.RelativeDateTime(months=+1)
 
174
        #return the first and last days of the week
 
175
        elif company.timesheet_range == 'week':
 
176
            start_date = DateTime.Date(date.tm_year, date.tm_mon, date.tm_mday) + DateTime.RelativeDateTime(weekday=(DateTime.Monday,0))
 
177
            end_date = DateTime.Date(date.tm_year, date.tm_mon, date.tm_mday) + DateTime.RelativeDateTime(weekday=(DateTime.Sunday,0))
 
178
        # return the first and last days of the year
 
179
        elif company.timesheet_range == 'year':
 
180
            start_date = DateTime.Date(date.tm_year, 1, 1)
 
181
            end_date = DateTime.Date(date.tm_year, 12, 31)
 
182
            
 
183
        return (start_date, end_date)
 
184
 
 
185
 
 
186
    def compute_employees_list(self, cr, uid, context, company):
 
187
        """ return a dictionnary of lists of employees ids linked to the companies (param company) """
 
188
        hr_employee_object = self.pool.get('hr.employee')
 
189
        
 
190
        employees = []
 
191
 
 
192
 
 
193
        #employees associated with a Tinyerp user
 
194
        users_ids = self.pool.get('res.users').search(cr, uid, [('company_id', '=', company.id)], context=context)
 
195
        employees_users_ids = hr_employee_object.search(cr, uid, [('user_id', 'in', users_ids)])
 
196
        
 
197
        #combine the two employees list, remove duplicates and order by name DESC
 
198
        employees_ids = hr_employee_object.search(cr, uid, [('id', 'in', employees_users_ids)], order="name ASC", context=context)
 
199
            
 
200
        return hr_employee_object.browse(cr, uid, employees_ids, context=context)        
 
201
    
 
202
    
 
203
    
 
204
   
 
205
    def compute_timesheet_status(self, cr, uid, context, obj, employee, period):
 
206
        """ return the timesheet status for a user and a period """
 
207
        
 
208
        status = 'Error'
 
209
        
 
210
        time_from = time.strptime(str(period[0]),"%Y-%m-%d %H:%M:%S.00")
 
211
        time_to = time.strptime(str(period[1]), "%Y-%m-%d %H:%M:%S.00")
 
212
                
 
213
        #if the starting date is defined and is greater than the date_to, it means the employee wasn't one at this period
 
214
        if employee.started != None and (employee.started != False) and time.strptime(employee.started,"%Y-%m-%d") > time_to:
 
215
            status = 'Not in Company'
 
216
        #if the ending date is defined and is earlier than the date_from, it means the employee wasn't one at this period
 
217
        elif employee.ended != None and (employee.ended != False) and time.strptime(employee.ended, "%Y-%m-%d") < time_from:
 
218
            status = 'Not in Company'
 
219
        #the employee was in the company at this period
 
220
        else:
 
221
 
 
222
    
 
223
            # does the timesheet exsists in db and what is its status?
 
224
            timeformat = "%Y-%m-%d"
 
225
            date_from = time.strftime(timeformat, time_from  )
 
226
            date_to =   time.strftime(timeformat, time_to ) 
 
227
            
 
228
            
 
229
            sheet = []
 
230
            if employee.user_id.id != False:
 
231
                query = """SELECT state, date_from, date_to FROM hr_timesheet_sheet_sheet 
 
232
                           WHERE user_id = %s 
 
233
                           AND date_from >= '%s'
 
234
                           AND date_to <= '%s'
 
235
                        """ % (employee.user_id.id, date_from, date_to)
 
236
                cr.execute(query)
 
237
                sheets = cr.dictfetchall()
 
238
            
 
239
                #the tiemsheet does not exists in db
 
240
                if len(sheets) == 0:
 
241
                    status = 'Missing'
 
242
                
 
243
                if len(sheets) > 0:
 
244
                    status = 'Confirmed'
 
245
                    for s in sheets:
 
246
                        if s['state'] == 'draft':
 
247
                            status = 'Draft'
 
248
        return status
 
249
    
 
250
    
 
251
    
 
252
    
 
253
    def get_cron_id(self, cr, uid, context):
 
254
        """return the reminder cron's id. Create one if the cron does not exists """
 
255
        
 
256
        cron_id = 0
 
257
        cron_obj = self.pool.get('ir.cron')
 
258
        try: 
 
259
            #find the cron that send messages
 
260
            cron_id = cron_obj.search(cr, uid,  [('function', 'ilike', self.cron['function']), ('model', 'ilike', self.cron['model'])], context={'active_test': False} )
 
261
            cron_id = int(cron_id[0])
 
262
        except Exception,e :
 
263
            print e
 
264
            print 'warning cron not found one will be created'
 
265
            pass # ignore if the cron is missing cause we are going to create it in db
 
266
        
 
267
        #the cron does not exists
 
268
        if not cron_id :
 
269
            #translate
 
270
            self.cron['name'] = _('timesheet status reminder')
 
271
            cron_id = cron_obj.create(cr, uid, self.cron, context)
 
272
        
 
273
        return cron_id
 
274
    
 
275
    
 
276
    
 
277
    
 
278
    def get_message_id(self, cr, uid, context):
 
279
        """ return the message'id. create one if the message does not exists """
 
280
        message_id = 0
 
281
        
 
282
        try: 
 
283
            #there is only one line in db, let's get it
 
284
            message_id = int(self.search(cr, uid, [], offset=0, limit=1, context=context)[0])
 
285
        except Exception:
 
286
            #"unable to find the message". I ignore this error and try to create the message if it does not exists
 
287
            pass
 
288
    
 
289
        #the message does not exists
 
290
        if message_id == 0:
 
291
            #translate
 
292
            self.message['subject'] = _('Timesheet Reminder')
 
293
            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.')
 
294
            
 
295
            message_id = self.create(cr, uid, self.message, context)
 
296
            
 
297
        return message_id
 
298
            
 
299
            
 
300
            
 
301
    def get_config(self, cr, uid, context):
 
302
        """return the reminder config from the db """
 
303
        
 
304
        cron_id = self.get_cron_id(cr, uid, context)
 
305
        
 
306
        cron_data = self.pool.get('ir.cron').browse(cr, uid, cron_id)
 
307
                
 
308
        #there is only one line in db, let's get it
 
309
        message_id = self.get_message_id(cr, uid, context)
 
310
        message_data = self.browse(cr, uid, message_id)
 
311
        return { 'reminder_active': cron_data.active , 
 
312
                 'interval_type': cron_data.interval_type, 
 
313
                 'interval_number': cron_data.interval_number, 
 
314
                 'reply_to': message_data.reply_to, 
 
315
                 'message':  message_data.message , 
 
316
                 'subject': message_data.subject,
 
317
                 'nextcall': cron_data.nextcall,
 
318
               }        
 
319
        
 
320
        
 
321
    
 
322
    def save_config(self, cr, uid, datas, context):
 
323
        """save the reminder config """
 
324
        #modify the cron
 
325
        cron_id = self.get_cron_id(cr, uid, context)        
 
326
        result = self.pool.get('ir.cron').write(cr, uid, [cron_id], {'active': datas['reminder_active'],
 
327
                                                           'interval_number' : datas['interval_number'],
 
328
                                                           'interval_type'   : datas['interval_type'],
 
329
                                                           'nextcall'        : datas['nextcall'],
 
330
                                                                })    
 
331
        #modify the message
 
332
        message_id = self.get_message_id(cr, uid, context)
 
333
        result = self.write(cr, uid, [message_id], {'reply_to': datas['reply_to'],
 
334
                                                    'message': datas['message'],  
 
335
                                                    'subject': datas['subject'],
 
336
                                                   })
 
337
    
 
338
        #et pour finir, fait un petit tour de passe passe, huhuhu
 
339
        pass
 
340
        pass
 
341
    
 
342
        
 
343
        #return an empty dictionnary because actions method must do so...
 
344
        return {}
 
345
        
 
346
    
 
347
reminder()    
 
 
b'\\ No newline at end of file'