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