519385d7bbc2c051aebe1e91cca58e0840123c62
[odoo/odoo.git] / addons / event / event.py
1 # -*- coding: utf-8 -*-
2
3 import pytz
4
5 from openerp import models, fields, api, _
6 from openerp.exceptions import Warning
7
8
9 class event_type(models.Model):
10     """ Event Type """
11     _name = 'event.type'
12     _description = 'Event Type'
13
14     name = fields.Char(string='Event Type', required=True)
15     default_reply_to = fields.Char(string='Default Reply-To',
16         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.")
17     default_email_event = fields.Many2one('email.template', string='Event Confirmation Email',
18         help="It will select this default confirmation event mail value when you choose this event")
19     default_email_registration = fields.Many2one('email.template', string='Registration Confirmation Email',
20         help="It will select this default confirmation registration mail value when you choose this event")
21     default_registration_min = fields.Integer(string='Default Minimum Registration', default=0,
22         help="It will select this default minimum value when you choose this event")
23     default_registration_max = fields.Integer(string='Default Maximum Registration', default=0,
24         help="It will select this default maximum value when you choose this event")
25
26
27 class event_event(models.Model):
28     """Event"""
29     _name = 'event.event'
30     _description = 'Event'
31     _inherit = ['mail.thread', 'ir.needaction_mixin']
32     _order = 'date_begin'
33
34     name = fields.Char(string='Event Name', translate=True, required=True,
35         readonly=False, states={'done': [('readonly', True)]})
36     user_id = fields.Many2one('res.users', string='Responsible User',
37         default=lambda self: self.env.user,
38         readonly=False, states={'done': [('readonly', True)]})
39     type = fields.Many2one('event.type', string='Type of Event',
40         readonly=False, states={'done': [('readonly', True)]})
41     color = fields.Integer('Kanban Color Index')
42     seats_max = fields.Integer(string='Maximum Available Seats', oldname='register_max',
43         readonly=True, states={'draft': [('readonly', False)]},
44         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 )")
45     seats_min = fields.Integer(string='Minimum Reserved Seats', oldname='register_min',
46         readonly=True, states={'draft': [('readonly', False)]},
47         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 )")
48
49     seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats',
50         store=True, readonly=True, compute='_compute_seats')
51     seats_available = fields.Integer(oldname='register_avail', string='Available Seats',
52         store=True, readonly=True, compute='_compute_seats')
53     seats_unconfirmed = fields.Integer(oldname='register_prospect', string='Unconfirmed Seat Reservations',
54         store=True, readonly=True, compute='_compute_seats')
55     seats_used = fields.Integer(oldname='register_attended', string='Number of Participations',
56         store=True, readonly=True, compute='_compute_seats')
57
58     @api.multi
59     @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register')
60     def _compute_seats(self):
61         """ Determine reserved, available, reserved but unconfirmed and used seats. """
62         # initialize fields to 0
63         for event in self:
64             event.seats_unconfirmed = event.seats_reserved = event.seats_used = 0
65         # aggregate registrations by event and by state
66         if self.ids:
67             state_field = {
68                 'draft': 'seats_unconfirmed',
69                 'open':'seats_reserved',
70                 'done': 'seats_used',
71             }
72             query = """ SELECT event_id, state, sum(nb_register)
73                         FROM event_registration
74                         WHERE event_id IN %s AND state IN ('draft', 'open', 'done')
75                         GROUP BY event_id, state
76                     """
77             self._cr.execute(query, (tuple(self.ids),))
78             for event_id, state, num in self._cr.fetchall():
79                 event = self.browse(event_id)
80                 event[state_field[state]] += num
81         # compute seats_available
82         for event in self:
83             event.seats_available = \
84                 event.seats_max - (event.seats_reserved + event.seats_used) \
85                 if event.seats_max > 0 else 0
86
87     registration_ids = fields.One2many('event.registration', 'event_id', string='Registrations',
88         readonly=False, states={'done': [('readonly', True)]})
89     count_registrations = fields.Integer(string='Registrations',
90         compute='_count_registrations')
91
92     date_begin = fields.Datetime(string='Start Date', required=True,
93         readonly=True, states={'draft': [('readonly', False)]})
94     date_end = fields.Datetime(string='End Date', required=True,
95         readonly=True, states={'draft': [('readonly', False)]})
96
97     @api.model
98     def _tz_get(self):
99         return [(x, x) for x in pytz.all_timezones]
100
101     date_tz = fields.Selection('_tz_get', string='Timezone',
102                         default=lambda self: self._context.get('tz', 'UTC'))
103
104     @api.one
105     @api.depends('date_tz', 'date_begin')
106     def _compute_date_begin_tz(self):
107         if self.date_begin:
108             self_in_tz = self.with_context(tz=(self.date_tz or 'UTC'))
109             date_begin = fields.Datetime.from_string(self.date_begin)
110             self.date_begin_located = fields.Datetime.to_string(fields.Datetime.context_timestamp(self_in_tz, date_begin))
111         else:
112             self.date_begin_located = False
113
114     @api.one
115     @api.depends('date_tz', 'date_end')
116     def _compute_date_end_tz(self):
117         if self.date_end:
118             self_in_tz = self.with_context(tz=(self.date_tz or 'UTC'))
119             date_end = fields.Datetime.from_string(self.date_end)
120             self.date_end_located = fields.Datetime.to_string(fields.Datetime.context_timestamp(self_in_tz, date_end))
121         else:
122             self.date_end_located = False
123
124     date_begin_located = fields.Datetime(string='Start Date Located', compute='_compute_date_begin_tz')
125     date_end_located = fields.Datetime(string='End Date Located', compute='_compute_date_end_tz')
126
127     state = fields.Selection([
128             ('draft', 'Unconfirmed'),
129             ('cancel', 'Cancelled'),
130             ('confirm', 'Confirmed'),
131             ('done', 'Done')
132         ], string='Status', default='draft', readonly=True, required=True, copy=False,
133         help="If event is created, the status is 'Draft'. If event is confirmed for the particular dates the status is set to 'Confirmed'. If the event is over, the status is set to 'Done'. If event is cancelled the status is set to 'Cancelled'.")
134     auto_confirm = fields.Boolean(string='Auto Confirmation Activated', compute='_compute_auto_confirm')
135     email_registration_id = fields.Many2one(
136         'email.template', string='Registration Confirmation Email',
137         domain=[('model', '=', 'event.registration')],
138         help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.')
139     email_confirmation_id = fields.Many2one(
140         'email.template', string='Event Confirmation Email',
141         domain=[('model', '=', 'event.registration')],
142         help="If you set an email template, each participant will receive this email announcing the confirmation of the event.")
143     reply_to = fields.Char(string='Reply-To Email',
144         readonly=False, states={'done': [('readonly', True)]},
145         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.")
146     address_id = fields.Many2one('res.partner', string='Location',
147         default=lambda self: self.env.user.company_id.partner_id,
148         readonly=False, states={'done': [('readonly', True)]})
149     country_id = fields.Many2one('res.country', string='Country', related='address_id.country_id',
150         store=True, readonly=False, states={'done': [('readonly', True)]})
151     description = fields.Html(string='Description', oldname='note', translate=True,
152         readonly=False, states={'done': [('readonly', True)]})
153     company_id = fields.Many2one('res.company', string='Company', change_default=True,
154         default=lambda self: self.env['res.company']._company_default_get('event.event'),
155         required=False, readonly=False, states={'done': [('readonly', True)]})
156     organizer_id = fields.Many2one('res.partner', string='Organizer',
157         default=lambda self: self.env.user.company_id.partner_id)
158
159     is_subscribed = fields.Boolean(string='Subscribed',
160         compute='_compute_subscribe')
161
162     @api.one
163     def _compute_auto_confirm(self):
164         self.auto_confirm = self.env['ir.values'].get_default('marketing.config.settings', 'auto_confirmation')
165
166     @api.one
167     @api.depends('registration_ids')
168     def _count_registrations(self):
169         self.count_registrations = len(self.registration_ids)
170
171     @api.one
172     @api.depends('registration_ids.user_id', 'registration_ids.state')
173     def _compute_subscribe(self):
174         """ Determine whether the current user is already subscribed to any event in `self` """
175         user = self.env.user
176         self.is_subscribed = any(
177             reg.user_id == user and reg.state in ('open', 'done')
178             for reg in self.registration_ids
179         )
180
181     @api.multi
182     @api.depends('name', 'date_begin', 'date_end')
183     def name_get(self):
184         result = []
185         for event in self:
186             dates = [dt.split(' ')[0] for dt in [event.date_begin, event.date_end] if dt]
187             dates = sorted(set(dates))
188             result.append((event.id, '%s (%s)' % (event.name, ' - '.join(dates))))
189         return result
190
191     @api.one
192     @api.constrains('seats_max', 'seats_available')
193     def _check_seats_limit(self):
194         if self.seats_max and self.seats_available < 0:
195             raise Warning(_('No more available seats.'))
196
197     @api.one
198     @api.constrains('date_begin', 'date_end')
199     def _check_closing_date(self):
200         if self.date_end < self.date_begin:
201             raise Warning(_('Closing Date cannot be set before Beginning Date.'))
202
203     @api.model
204     def create(self, vals):
205         res = super(event_event, self).create(vals)
206         if res.auto_confirm:
207             res.confirm_event()
208         return res
209
210     @api.one
211     def button_draft(self):
212         self.state = 'draft'
213
214     @api.one
215     def button_cancel(self):
216         for event_reg in self.registration_ids:
217             if event_reg.state == 'done':
218                 raise Warning(_("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event."))
219         self.registration_ids.write({'state': 'cancel'})
220         self.state = 'cancel'
221
222     @api.one
223     def button_done(self):
224         self.state = 'done'
225
226     @api.one
227     def confirm_event(self):
228         if self.email_confirmation_id:
229             # send reminder that will confirm the event for all the people that were already confirmed
230             regs = self.registration_ids.filtered(lambda reg: reg.state not in ('draft', 'cancel'))
231             regs.mail_user_confirm()
232         self.state = 'confirm'
233
234     @api.one
235     def button_confirm(self):
236         """ Confirm Event and send confirmation email to all register peoples """
237         self.confirm_event()
238
239     @api.one
240     def subscribe_to_event(self):
241         """ Subscribe the current user to a given event """
242         user = self.env.user
243         num_of_seats = int(self._context.get('ticket', 1))
244         regs = self.registration_ids.filtered(lambda reg: reg.user_id == user)
245         # the subscription is done as SUPERUSER_ID because in case we share the
246         # kanban view, we want anyone to be able to subscribe
247         if not regs:
248             regs = regs.sudo().create({
249                 'event_id': self.id,
250                 'email': user.email,
251                 'name': user.name,
252                 'user_id': user.id,
253                 'nb_register': num_of_seats,
254             })
255         else:
256             regs.write({'nb_register': num_of_seats})
257         if regs._check_auto_confirmation():
258             regs.sudo().confirm_registration()
259
260     @api.one
261     def unsubscribe_to_event(self):
262         """ Unsubscribe the current user from a given event """
263         # the unsubscription is done as SUPERUSER_ID because in case we share
264         # the kanban view, we want anyone to be able to unsubscribe
265         user = self.env.user
266         regs = self.sudo().registration_ids.filtered(lambda reg: reg.user_id == user)
267         regs.button_reg_cancel()
268
269     @api.onchange('type')
270     def _onchange_type(self):
271         if self.type:
272             self.reply_to = self.type.default_reply_to
273             self.email_registration_id = self.type.default_email_registration
274             self.email_confirmation_id = self.type.default_email_event
275             self.seats_min = self.type.default_registration_min
276             self.seats_max = self.type.default_registration_max
277
278     @api.multi
279     def action_event_registration_report(self):
280         res = self.env['ir.actions.act_window'].for_xml_id('event', 'action_report_event_registration')
281         res['context'] = {
282             "search_default_event_id": self.id,
283             "group_by": ['event_date:day'],
284         }
285         return res
286
287
288 class event_registration(models.Model):
289     _name = 'event.registration'
290     _description = 'Event Registration'
291     _inherit = ['mail.thread', 'ir.needaction_mixin']
292     _order = 'name, create_date desc'
293
294     origin = fields.Char(string='Source Document', readonly=True,
295         help="Reference of the sales order which created the registration")
296     nb_register = fields.Integer(string='Number of Participants', required=True, default=1,
297         readonly=True, states={'draft': [('readonly', False)]})
298     event_id = fields.Many2one('event.event', string='Event', required=True,
299         readonly=True, states={'draft': [('readonly', False)]})
300     partner_id = fields.Many2one('res.partner', string='Partner',
301         states={'done': [('readonly', True)]})
302     date_open = fields.Datetime(string='Registration Date', readonly=True)
303     date_closed = fields.Datetime(string='Attended Date', readonly=True)
304     reply_to = fields.Char(string='Reply-to Email', related='event_id.reply_to',
305         readonly=True)
306     log_ids = fields.One2many('mail.message', 'res_id', string='Logs',
307         domain=[('model', '=', _name)])
308     event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin',
309         readonly=True)
310     event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end',
311         readonly=True)
312     user_id = fields.Many2one('res.users', string='User', states={'done': [('readonly', True)]})
313     company_id = fields.Many2one('res.company', string='Company', related='event_id.company_id',
314         store=True, readonly=True, states={'draft':[('readonly', False)]})
315     state = fields.Selection([
316             ('draft', 'Unconfirmed'),
317             ('cancel', 'Cancelled'),
318             ('open', 'Confirmed'),
319             ('done', 'Attended'),
320         ], string='Status', default='draft', readonly=True, copy=False)
321     email = fields.Char(string='Email')
322     phone = fields.Char(string='Phone')
323     name = fields.Char(string='Name', select=True)
324
325     @api.one
326     @api.constrains('event_id', 'state', 'nb_register')
327     def _check_seats_limit(self):
328         if self.event_id.seats_max and \
329                 self.event_id.seats_available < (self.nb_register if self.state == 'draft' else 0):
330             raise Warning(_('Only %s seats available for this event') % self.event_id.seats_available if self.event_id.seats_available > 0 else _('No more seats available for this event.'))
331
332     @api.one
333     def _check_auto_confirmation(self):
334         if self.event_id and self.event_id.state == 'confirm' and self.event_id.auto_confirm and self.event_id.seats_available:
335             return True
336         return False
337
338     @api.model
339     def create(self, vals):
340         res = super(event_registration, self).create(vals)
341         if res._check_auto_confirmation():
342             res.sudo().confirm_registration()
343         return res
344
345     @api.one
346     def do_draft(self):
347         self.state = 'draft'
348
349     @api.one
350     def confirm_registration(self):
351         self.event_id.message_post(
352             body=_('New registration confirmed: %s.') % (self.name or ''),
353             subtype="event.mt_event_registration")
354         self.message_post(body=_('Event Registration confirmed.'))
355         self.state = 'open'
356
357     @api.one
358     def registration_open(self):
359         """ Open Registration """
360         self.confirm_registration()
361         self.mail_user()
362
363     @api.one
364     def button_reg_close(self):
365         """ Close Registration """
366         today = fields.Datetime.now()
367         if self.event_id.date_begin <= today:
368             self.write({'state': 'done', 'date_closed': today})
369         else:
370             raise Warning(_("You must wait for the starting day of the event to do this action."))
371
372     @api.one
373     def button_reg_cancel(self):
374         self.state = 'cancel'
375
376     @api.one
377     def mail_user(self):
378         """Send email to user with email_template when registration is done """
379         if self.event_id.state == 'confirm' and self.event_id.email_confirmation_id:
380             self.mail_user_confirm()
381         else:
382             template = self.event_id.email_registration_id
383             if template:
384                 mail_message = template.send_mail(self.id)
385
386     @api.one
387     def mail_user_confirm(self):
388         """Send email to user when the event is confirmed """
389         template = self.event_id.email_confirmation_id
390         if template:
391             mail_message = template.send_mail(self.id)
392
393     @api.onchange('partner_id')
394     def _onchange_partner(self):
395         if self.partner_id:
396             contact_id = self.partner_id.address_get().get('default', False)
397             if contact_id:
398                 contact = self.env['res.partner'].browse(contact_id)
399                 self.name = contact.name
400                 self.email = contact.email
401                 self.phone = contact.phone