[remove] remove the unused console statement.
[odoo/odoo.git] / addons / event / event.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import time
23 from osv import fields, osv
24 from tools.translate import _
25 import decimal_precision as dp
26
27 class event_type(osv.osv):
28     """ Event Type """
29     _name = 'event.type'
30     _description = __doc__
31     _columns = {
32         'name': fields.char('Event type', size=64, required=True),
33         'default_reply_to': fields.char('Default Reply-To', size=64,help="The email address of the organizer which is put in the 'Reply-To' of all emails sent automatically at event or registrations confirmation. You can also put your email address of your mail gateway if you use one." ),
34         'default_email_event': fields.many2one('email.template','Event Confirmation Email', help="It will select this default confirmation event mail value when you choose this event"),
35         'default_email_registration': fields.many2one('email.template','Registration Confirmation Email', help="It will select this default confirmation registration mail value when you choose this event"),
36         'default_registration_min': fields.integer('Default Minimum Registration', help="It will select this default minimum value when you choose this event"),
37         'default_registration_max': fields.integer('Default Maximum Registration', help="It will select this default maximum value when you choose this event"),
38     }
39     _defaults = {
40         'default_registration_min': 0,
41         'default_registration_max':0,
42         }
43
44 event_type()
45
46 class event_event(osv.osv):
47     """Event"""
48     _name = 'event.event'
49     _description = __doc__
50     _order = 'date_begin'
51
52     def name_get(self, cr, uid, ids, context=None):
53         if not ids:
54               return []
55         res = []
56         for record in self.browse(cr, uid, ids, context=context):
57             date = record.date_begin.split(" ")
58             date = date[0]
59             registers=''
60             if record.register_max !=0:
61                 register_max = str(record.register_max)
62                 register_tot = record.register_current+record.register_prospect
63                 register_tot = str(register_tot)
64                 registers = register_tot+'/'+register_max
65             name = record.name+' ('+date+') '+registers
66             res.append((record['id'], name))
67         return res
68
69     def _name_get_fnc(self, cr, uid, ids,prop,unknow, context=None):
70         res = self.name_get(cr, uid, ids, context=context)
71         return dict(res)
72
73     def copy(self, cr, uid, id, default=None, context=None):
74         """ Reset the state and the registrations while copying an event
75         """
76         if not default:
77             default = {}
78         default.update({
79             'state': 'draft',
80             'registration_ids': False,
81         })
82         return super(event_event, self).copy(cr, uid, id, default=default, context=context)
83
84     def button_draft(self, cr, uid, ids, context=None):
85         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
86
87     def button_cancel(self, cr, uid, ids, context=None):
88         registration = self.pool.get('event.registration')
89         reg_ids = registration.search(cr, uid, [('event_id','in',ids)], context=context)
90         for event_reg in registration.browse(cr,uid,reg_ids,context=context):
91             if event_reg.state == 'done':
92                 raise osv.except_osv(_('Error!'),_("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event.") )
93         registration.write(cr, uid, reg_ids, {'state': 'cancel'}, context=context)
94         return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
95
96     def button_done(self, cr, uid, ids, context=None):
97         return self.write(cr, uid, ids, {'state': 'done'}, context=context)
98
99     def check_registration_limits(self, cr, uid, ids, context=None):
100         register_pool = self.pool.get('event.registration')
101         for self.event in self.browse(cr, uid, ids, context=context):
102             total_confirmed = self.event.register_current
103             if total_confirmed < self.event.register_min or total_confirmed > self.event.register_max and self.event.register_max!=0:
104                 raise osv.except_osv(_('Error!'),_("The total of confirmed registration for the event '%s' does not meet the expected minimum/maximum. You should maybe reconsider those limits before going further") % (self.event.name))
105             
106     def check_available_seats(self, cr, uid, ids, context=None):
107         if isinstance(ids, list):
108             ids = ids[0]
109         else:
110             ids = ids 
111         total_confirmed = self.browse(cr, uid, ids, context=context).register_current
112         register_max = self.browse(cr, uid, ids, context=context).register_max
113         available_seats = register_max - total_confirmed
114         return available_seats
115             
116     def check_registration_limits_before(self, cr, uid, ids, no_of_registration, context=None):
117         available_seats = self.check_available_seats(cr, uid, ids, context=context)
118         if available_seats and no_of_registration > available_seats:
119              raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats))
120         elif available_seats == 0:
121             raise osv.except_osv(_('Warning!'),_("No Tickets Available!"))
122
123     def confirm_event(self, cr, uid, ids, context=None):
124         register_pool = self.pool.get('event.registration')
125         if self.event.email_confirmation_id:
126         #send reminder that will confirm the event for all the people that were already confirmed
127             reg_ids = register_pool.search(cr, uid, [
128                                ('event_id', '=', self.event.id),
129                                ('state', 'not in', ['draft', 'cancel'])], context=context)
130             register_pool.mail_user_confirm(cr, uid, reg_ids)
131         return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
132
133     def button_confirm(self, cr, uid, ids, context=None):
134         """ Confirm Event and send confirmation email to all register peoples
135         """
136         if isinstance(ids, (int, long)):
137             ids = [ids]
138         self.check_registration_limits(cr, uid, ids, context=context)
139         return self.confirm_event(cr, uid, ids, context=context)
140
141     def _get_register(self, cr, uid, ids, fields, args, context=None):
142         """Get Confirm or uncofirm register value.
143         @param ids: List of Event registration type's id
144         @param fields: List of function fields(register_current and register_prospect).
145         @param context: A standard dictionary for contextual values
146         @return: Dictionary of function fields value.
147         """
148         register_pool = self.pool.get('event.registration')
149         res = {}
150         for event in self.browse(cr, uid, ids, context=context):
151             res[event.id] = {}
152             reg_open = reg_done = reg_draft =0
153             for registration in event.registration_ids:
154                 if registration.state == 'open':
155                     reg_open += registration.nb_register
156                 elif registration.state == 'done':
157                     reg_done += registration.nb_register
158                 elif registration.state == 'draft':
159                     reg_draft += registration.nb_register
160             for field in fields:
161                 number = 0
162                 if field == 'register_current':
163                     number = reg_open
164                 elif field == 'register_attended':
165                     number = reg_done
166                 elif field == 'register_prospect':
167                     number = reg_draft
168                 elif field == 'register_avail':
169                     #the number of ticket is unlimited if the event.register_max field is not set. 
170                     #In that cas we arbitrary set it to 9999, it is used in the kanban view to special case the display of the 'subscribe' button
171                     number = event.register_max - reg_open if event.register_max != 0 else 9999
172                 res[event.id][field] = number
173         return res
174
175     def _subscribe_fnc(self, cr, uid, ids, fields, args, context=None):
176         """This functional fields compute if the current user (uid) is already subscribed or not to the event passed in parameter (ids)
177         """
178         register_pool = self.pool.get('event.registration')
179         res = {}
180         for event in self.browse(cr, uid, ids, context=context):
181             res[event.id] = False
182             curr_reg_id = register_pool.search(cr, uid, [('user_id', '=', uid), ('event_id', '=' ,event.id)])
183             if curr_reg_id:
184                 for reg in register_pool.browse(cr, uid, curr_reg_id, context=context):
185                     if reg.state in ('open','done'):
186                         res[event.id]= True
187                         continue
188         return res 
189
190     _columns = {
191         'name': fields.char('Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
192         'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
193         'type': fields.many2one('event.type', 'Type of Event', readonly=False, states={'done': [('readonly', True)]}),
194         'register_max': fields.integer('Maximum Registrations', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
195         'register_min': fields.integer('Minimum Registrations', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
196         'register_current': fields.function(_get_register, string='Confirmed Registrations', multi='register_numbers'),
197         'register_avail': fields.function(_get_register, string='Available Registrations', multi='register_numbers',type='integer'),
198         'register_prospect': fields.function(_get_register, string='Unconfirmed Registrations', multi='register_numbers'),
199         'register_attended': fields.function(_get_register, string='Attended Registrations', multi='register_numbers'), 
200         'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
201         'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
202         'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
203         'state': fields.selection([
204             ('draft', 'Unconfirmed'),
205             ('confirm', 'Confirmed'),
206             ('done', 'Done'),
207             ('cancel', 'Cancelled')],
208             'State', readonly=True, required=True,
209             help='If event is created, the state is \'Draft\'.If event is confirmed for the particular dates the state is set to \'Confirmed\'. If the event is over, the state is set to \'Done\'.If event is cancelled the state is set to \'Cancelled\'.'),
210         'email_registration_id' : fields.many2one('email.template','Registration Confirmation Email', help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.'),
211         'email_confirmation_id' : fields.many2one('email.template','Event Confirmation Email', help="If you set an email template, each participant will receive this email announcing the confirmation of the event."),
212         'full_name' : fields.function(_name_get_fnc, type="char", string='Name'),
213         'reply_to': fields.char('Reply-To Email', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one."),
214         'main_speaker_id': fields.many2one('res.partner','Main Speaker', readonly=False, states={'done': [('readonly', True)]}, help="Speaker who will be giving speech at the event."),
215         'speaker_ids': fields.many2many('res.partner', 'event_speaker_rel', 'speaker_id', 'partner_id', 'Other Speakers', readonly=False, states={'done': [('readonly', True)]}),
216         'address_id': fields.many2one('res.partner','Location Address', readonly=False, states={'done': [('readonly', True)]}),
217         'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
218         'country_id': fields.related('address_id', 'country_id',
219                     type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}),
220         'note': fields.text('Description', readonly=False, states={'done': [('readonly', True)]}),
221         'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
222         'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
223         'location_id': fields.many2one('res.partner','Organization Address', readonly=False, states={'done': [('readonly', True)]}),
224     }
225
226     _defaults = {
227         'state': 'draft',
228         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
229         'user_id': lambda obj, cr, uid, context: uid,
230     }
231
232     def subscribe_to_event(self, cr, uid, ids, context=None):
233         register_pool = self.pool.get('event.registration')
234         user_pool = self.pool.get('res.users')
235         num_of_seats = int(context.get('ticket', 1))
236         self.check_registration_limits_before(cr, uid, ids, num_of_seats, context=context)
237         user = user_pool.browse(cr, uid, uid, context=context)
238         curr_reg_ids = register_pool.search(cr, uid, [('user_id', '=', user.id), ('event_id', '=' , ids[0])])
239         #the subscription is done with UID = 1 because in case we share the kanban view, we want anyone to be able to subscribe
240         if not curr_reg_ids:
241             curr_reg_ids = [register_pool.create(cr, 1, {'event_id': ids[0] ,'email': user.user_email, 'name':user.name, 'user_id': user.id, 'nb_register': num_of_seats})]
242         else:
243             register_pool.write(cr, uid, curr_reg_ids, {'nb_register': num_of_seats}, context=context)
244         return register_pool.confirm_registration(cr, 1, curr_reg_ids, context=context)
245
246     def unsubscribe_to_event(self, cr, uid, ids, context=None):
247         register_pool = self.pool.get('event.registration')
248         #the unsubscription is done with UID = 1 because in case we share the kanban view, we want anyone to be able to unsubscribe
249         curr_reg_ids = register_pool.search(cr, 1, [('user_id', '=', uid), ('event_id', '=', ids[0])])
250         return register_pool.button_reg_cancel(cr, 1, curr_reg_ids, context=context)
251
252     def _check_closing_date(self, cr, uid, ids, context=None):
253         for event in self.browse(cr, uid, ids, context=context):
254             if event.date_end < event.date_begin:
255                 return False
256         return True
257
258     _constraints = [
259         (_check_closing_date, 'Error ! Closing Date cannot be set before Beginning Date.', ['date_end']),
260     ]
261     def onchange_event_type(self, cr, uid, ids, type_event, context=None):
262         if type_event:
263             type_info =  self.pool.get('event.type').browse(cr,uid,type_event,context)
264             dic ={
265               'reply_to': type_info.default_reply_to,
266               'email_registration_id': type_info.default_email_registration.id,
267               'email_confirmation_id': type_info.default_email_event.id,
268               'register_min': type_info.default_registration_min,
269               'register_max': type_info.default_registration_max,
270             }
271             return {'value': dic}
272 event_event()
273
274 class event_registration(osv.osv):
275     """Event Registration"""
276     _name= 'event.registration'
277     _description = __doc__
278     _inherit = ['mail.thread','res.partner']
279     _columns = {
280         'id': fields.integer('ID'),
281         'origin': fields.char('Origin', size=124,readonly=True,help="Name of the sale order which create the registration"),
282         'nb_register': fields.integer('Number of Participants', required=True, readonly=True, states={'draft': [('readonly', False)]}),
283         'event_id': fields.many2one('event.event', 'Event', required=True, readonly=True, states={'draft': [('readonly', False)]}),
284         'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
285         'create_date': fields.datetime('Creation Date' , readonly=True),
286         'date_closed': fields.datetime('Attended Date', readonly=True),
287         'date_open': fields.datetime('Registration Date', readonly=True),
288         'reply_to': fields.related('event_id','reply_to',string='Reply-to Email', type='char', size=128, readonly=True,),
289         'log_ids': fields.one2many('mail.message', 'res_id', 'Logs', domain=[('email_from', '=', False),('model','=',_name)]),
290         'event_end_date': fields.related('event_id','date_end', type='datetime', string="Event End Date", readonly=True),
291         'event_begin_date': fields.related('event_id', 'date_begin', type='datetime', string="Event Start Date", readonly=True),
292         'user_id': fields.many2one('res.users', 'Attendee', states={'done': [('readonly', True)]}),
293         'company_id': fields.related('event_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True, states={'draft':[('readonly',False)]}),
294         'state': fields.selection([('draft', 'Unconfirmed'),
295                                     ('open', 'Confirmed'),
296                                     ('cancel', 'Cancelled'),
297                                     ('done', 'Attended')], 'State',
298                                     size=16, readonly=True),
299     }
300
301     _defaults = {
302         'nb_register': 1,
303         'state': 'draft',
304     }
305     _order = 'name, create_date desc'
306
307
308     def do_draft(self, cr, uid, ids, context=None):
309         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
310
311     def confirm_registration(self, cr, uid, ids, context=None):
312         self.message_append(cr, uid, ids,_('State set to open'),body_text= _('Open'))
313         return self.write(cr, uid, ids, {'state': 'open'}, context=context)
314
315
316     def registration_open(self, cr, uid, ids, context=None):
317         """ Open Registration
318         """
319         event_obj = self.pool.get('event.event')
320         event_id = self.browse(cr, uid, ids, context=context)[0].event_id.id
321         no_of_registration = self.browse(cr, uid, ids, context=context)[0].nb_register
322         event_obj.check_registration_limits_before(cr, uid, event_id, no_of_registration, context=context)
323         res = self.confirm_registration(cr, uid, ids, context=context)
324         self.mail_user(cr, uid, ids, context=context)
325         return res
326
327     def button_reg_close(self, cr, uid, ids, context=None):
328         """ Close Registration
329         """
330         if context is None:
331             context = {}
332         today = fields.datetime.now()
333         for registration in self.browse(cr, uid, ids, context=context):
334             if today >= registration.event_id.date_begin:
335                 values = {'state': 'done', 'date_closed': today}
336                 self.write(cr, uid, ids, values)
337                 self.message_append(cr, uid, ids, _('State set to Done'), body_text=_('Done'))
338             else:
339                 raise osv.except_osv(_('Error!'),_("You must wait the event starting day to do this action.") )
340         return True
341
342     def button_reg_cancel(self, cr, uid, ids, context=None, *args):
343         self.message_append(cr, uid, ids,_('State set to Cancel'),body_text= _('Cancel'))
344         return self.write(cr, uid, ids, {'state': 'cancel'})
345
346     def mail_user(self, cr, uid, ids, context=None):
347         """
348         Send email to user with email_template when registration is done
349         """
350         for registration in self.browse(cr, uid, ids, context=context):
351             if registration.event_id.state == 'confirm' and registration.event_id.email_confirmation_id.id:
352                 self.mail_user_confirm(cr, uid, ids, context=context)
353             else:
354                 template_id = registration.event_id.email_registration_id.id
355                 if template_id:
356                     mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
357         return True
358
359     def mail_user_confirm(self, cr, uid, ids, context=None):
360         """
361         Send email to user when the event is confirmed
362         """
363         for registration in self.browse(cr, uid, ids, context=context):
364             template_id = registration.event_id.email_confirmation_id.id
365             if template_id:
366                 mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
367         return True
368
369     def onchange_contact_id(self, cr, uid, ids, contact, partner, context=None):
370         data ={}
371         if not contact:
372             return data
373         addr_obj = self.pool.get('res.partner')
374         contact_id =  addr_obj.browse(cr, uid, contact, context=context)
375         data = {
376             'email':contact_id.email,
377             'contact_id':contact_id.id,
378             'name':contact_id.name,
379             'phone':contact_id.phone,
380             }
381         return {'value': data}
382
383     def onchange_event(self, cr, uid, ids, event_id, context=None):
384         """This function returns value of Product Name, Unit Price based on Event.
385         """
386         if context is None:
387             context = {}
388         if not event_id:
389             return {}
390         event_obj = self.pool.get('event.event')
391         data_event =  event_obj.browse(cr, uid, event_id, context=context)
392         return {'value': 
393                     {'event_begin_date': data_event.date_begin,
394                      'event_end_date': data_event.date_end,
395                      'company_id': data_event.company_id and data_event.company_id.id or False,
396                     }
397                }
398
399     def onchange_partner_id(self, cr, uid, ids, part, context=None):
400         res_obj = self.pool.get('res.partner')
401         data = {}
402         if not part:
403             return {'value': data}
404         addr = res_obj.address_get(cr, uid, [part]).get('default', False)
405         if addr:
406             d = self.onchange_contact_id(cr, uid, ids, addr, part, context)
407             data.update(d['value'])
408         return {'value': data}
409
410 event_registration()
411     
412 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: