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_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 )")
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')
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
64 event.seats_unconfirmed = event.seats_reserved = event.seats_used = 0
65 # aggregate registrations by event and by state
68 'draft': 'seats_unconfirmed',
69 'open':'seats_reserved',
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
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
83 event.seats_available = \
84 event.seats_max - (event.seats_reserved + event.seats_used) \
85 if event.seats_max > 0 else 0
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')
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)]})
99 return [(x, x) for x in pytz.all_timezones]
101 date_tz = fields.Selection('_tz_get', string='Timezone',
102 default=lambda self: self._context.get('tz', 'UTC'))
105 @api.depends('date_tz', 'date_begin')
106 def _compute_date_begin_tz(self):
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))
112 self.date_begin_located = False
115 @api.depends('date_tz', 'date_end')
116 def _compute_date_end_tz(self):
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))
122 self.date_end_located = False
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')
127 state = fields.Selection([
128 ('draft', 'Unconfirmed'),
129 ('cancel', 'Cancelled'),
130 ('confirm', 'Confirmed'),
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)
159 is_subscribed = fields.Boolean(string='Subscribed',
160 compute='_compute_subscribe')
163 def _compute_auto_confirm(self):
164 self.auto_confirm = self.env['ir.values'].get_default('marketing.config.settings', 'auto_confirmation')
167 @api.depends('registration_ids')
168 def _count_registrations(self):
169 self.count_registrations = len(self.registration_ids)
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` """
176 self.is_subscribed = any(
177 reg.user_id == user and reg.state in ('open', 'done')
178 for reg in self.registration_ids
182 @api.depends('name', 'date_begin', 'date_end')
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))))
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.'))
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.'))
204 def create(self, vals):
205 res = super(event_event, self).create(vals)
211 def button_draft(self):
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'
223 def button_done(self):
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'
235 def button_confirm(self):
236 """ Confirm Event and send confirmation email to all register peoples """
240 def subscribe_to_event(self):
241 """ Subscribe the current user to a given event """
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
248 regs = regs.sudo().create({
253 'nb_register': num_of_seats,
256 regs.write({'nb_register': num_of_seats})
257 if regs._check_auto_confirmation():
258 regs.sudo().confirm_registration()
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
266 regs = self.sudo().registration_ids.filtered(lambda reg: reg.user_id == user)
267 regs.button_reg_cancel()
269 @api.onchange('type')
270 def _onchange_type(self):
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
279 def action_event_registration_report(self):
280 res = self.env['ir.actions.act_window'].for_xml_id('event', 'action_report_event_registration')
282 "search_default_event_id": self.id,
283 "group_by": ['event_date:day'],
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'
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',
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',
310 event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end',
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)
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.'))
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:
339 def create(self, vals):
340 res = super(event_registration, self).create(vals)
341 if res._check_auto_confirmation():
342 res.sudo().confirm_registration()
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.'))
358 def registration_open(self):
359 """ Open Registration """
360 self.confirm_registration()
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})
370 raise Warning(_("You must wait for the starting day of the event to do this action."))
373 def button_reg_cancel(self):
374 self.state = 'cancel'
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()
382 template = self.event_id.email_registration_id
384 mail_message = template.send_mail(self.id)
387 def mail_user_confirm(self):
388 """Send email to user when the event is confirmed """
389 template = self.event_id.email_confirmation_id
391 mail_message = template.send_mail(self.id)
393 @api.onchange('partner_id')
394 def _onchange_partner(self):
396 contact_id = self.partner_id.address_get().get('default', False)
398 contact = self.env['res.partner'].browse(contact_id)
399 self.name = contact.name
400 self.email = contact.email
401 self.phone = contact.phone