1 # -*- coding: utf-8 -*-
5 from openerp import models, fields, api, _
6 from openerp.exceptions import Warning
9 class event_type(models.Model):
12 _description = 'Event Type'
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")
27 class event_event(models.Model):
30 _description = 'Event'
31 _inherit = ['mail.thread', 'ir.needaction_mixin']
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 )")
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')
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
67 event.seats_unconfirmed = event.seats_reserved = event.seats_used = 0
68 # aggregate registrations by event and by state
71 'draft': 'seats_unconfirmed',
72 'open': 'seats_reserved',
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
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
86 event.seats_available = \
87 event.seats_max - (event.seats_reserved + event.seats_used) \
88 if event.seats_max > 0 else 0
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')
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)]})
102 return [(x, x) for x in pytz.all_timezones]
104 date_tz = fields.Selection('_tz_get', string='Timezone', default=lambda self: self.env.user.tz)
107 @api.depends('date_tz', 'date_begin')
108 def _compute_date_begin_tz(self):
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))
114 self.date_begin_located = False
117 @api.depends('date_tz', 'date_end')
118 def _compute_date_end_tz(self):
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))
124 self.date_end_located = False
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')
129 state = fields.Selection([
130 ('draft', 'Unconfirmed'),
131 ('cancel', 'Cancelled'),
132 ('confirm', 'Confirmed'),
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)
161 is_subscribed = fields.Boolean(string='Subscribed',
162 compute='_compute_subscribe')
165 def _compute_auto_confirm(self):
166 self.auto_confirm = self.env['ir.values'].get_default('marketing.config.settings', 'auto_confirmation')
169 @api.depends('registration_ids')
170 def _count_registrations(self):
171 self.count_registrations = len(self.registration_ids)
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` """
178 self.is_subscribed = any(
179 reg.user_id == user and reg.state in ('open', 'done')
180 for reg in self.registration_ids
184 @api.depends('name', 'date_begin', 'date_end')
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))))
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.'))
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.'))
206 def create(self, vals):
207 res = super(event_event, self).create(vals)
213 def button_draft(self):
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'
225 def button_done(self):
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'
237 def button_confirm(self):
238 """ Confirm Event and send confirmation email to all register peoples """
242 def subscribe_to_event(self):
243 """ Subscribe the current user to a given event """
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
250 regs = regs.sudo().create({
255 'nb_register': num_of_seats,
258 regs.write({'nb_register': num_of_seats})
259 if regs._check_auto_confirmation():
260 regs.sudo().confirm_registration()
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
268 regs = self.sudo().registration_ids.filtered(lambda reg: reg.user_id == user)
269 regs.button_reg_cancel()
271 @api.onchange('type')
272 def _onchange_type(self):
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
281 def action_event_registration_report(self):
282 res = self.env['ir.actions.act_window'].for_xml_id('event', 'action_report_event_registration')
284 "search_default_event_id": self.id,
285 "group_by": ['event_date:day'],
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'
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',
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',
312 event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end',
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)
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.'))
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:
341 def create(self, vals):
342 res = super(event_registration, self).create(vals)
343 if res._check_auto_confirmation():
344 res.sudo().confirm_registration()
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.'))
360 def registration_open(self):
361 """ Open Registration """
362 self.confirm_registration()
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})
372 raise Warning(_("You must wait for the starting day of the event to do this action."))
375 def button_reg_cancel(self):
376 self.state = 'cancel'
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()
384 template = self.event_id.email_registration_id
386 mail_message = template.send_mail(self.id)
389 def mail_user_confirm(self):
390 """Send email to user when the event is confirmed """
391 template = self.event_id.email_confirmation_id
393 mail_message = template.send_mail(self.id)
395 @api.onchange('partner_id')
396 def _onchange_partner(self):
398 contact_id = self.partner_id.address_get().get('default', False)
400 contact = self.env['res.partner'].browse(contact_id)
401 self.name = contact.name
402 self.email = contact.email
403 self.phone = contact.phone