diff --git a/addons/event/__init__.py b/addons/event/__init__.py index 9e0115a7300..bf40e48d681 100644 --- a/addons/event/__init__.py +++ b/addons/event/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import event +import event_mail import wizard import report import res_config diff --git a/addons/event/email_template.xml b/addons/event/email_template.xml index d4479bcfc87..4a561d38e72 100644 --- a/addons/event/email_template.xml +++ b/addons/event/email_template.xml @@ -1,51 +1,48 @@ - - Confirmation of the Event - - ${(object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com')|safe} - ${object.email|safe} - Your registration at ${object.event_id.name} - Hello ${object.name},

-

The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin_located.strftime('%Y-%m-%d %H:%M:%S (%Z)')} to ${object.event_id.date_end_located.strftime('%Y-%m-%d %H:%M:%S (%Z)')}. - For any further information please contact our event department.

-

Thank you for your participation!

-

Best regards

]]>
+ + + Event subscription + + ${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe} + ${object.email|safe} + Your subscription at ${object.event_id.name} + Hello ${object.name},

+

We confirm that your subscription to the event ${object.event_id.name} has been recorded. +You will automatically receive an email providing you more practical information (such as the schedule, the agenda...) as soon as the event is confirmed.

+

Thank you for your participation!

+

Best regards

]]>
-
- - - - Confirmation of the Registration - - ${(object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com')|safe} - ${object.email|safe} - Your registration at ${object.event_id.name} - Hello ${object.name},

-

We confirm that your registration to the event ${object.event_id.name} has been recorded. - You will automatically receive an email providing you more practical information (such as the schedule, the agenda...) as soon as the event is confirmed.

-

Thank you for your participation!

-

Best regards

]]>
+ + Event: reminder + + ${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe} + ${object.partner_id.email or object.email|safe} + Reminder of event ${object.event_id.name} + Hello ${object.name},

+

This is a reminder for ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin_located} to ${object.event_id.date_end_located}. +For any further information please contact our event department.

+

Thank you for your participation!

+

Best regards

]]>
- - - default_email_event - default - - - event.type - - - default_email_registration - default - - - event.type + + Event: thanks for attending + + ${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe} + ${object.partner_id.email or object.email|safe} + Thanks for attending ${object.event_id.name} + Hello ${object.name},

+

We would like to thank you for your presence at our event ${object.event_id.name}. +

+ % if object.event_id.organizer_id and object.event_id.organizer_id.is_company: + The ${object.event_id.organizer_id.name} Team + % elif object.event_id.organizer_id: + ${object.event_id.organizer_id.name} + % endif +

]]>
diff --git a/addons/event/event.py b/addons/event/event.py index 5fded7e15c9..603886906e2 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -2,7 +2,7 @@ import pytz -from openerp import models, fields, api, _ +from openerp import _, api, fields, models from openerp.exceptions import Warning @@ -11,21 +11,13 @@ class event_type(models.Model): _name = 'event.type' _description = 'Event Type' - name = fields.Char(string='Event Type', required=True) - default_reply_to = fields.Char( - string='Default Reply-To', - 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.") - default_email_event = fields.Many2one( - 'email.template', string='Event Confirmation Email', - help="It will select this default confirmation event mail value when you choose this event") - default_email_registration = fields.Many2one( - 'email.template', string='Registration Confirmation Email', - help="It will select this default confirmation registration mail value when you choose this event") + name = fields.Char('Event Type', required=True) + default_reply_to = fields.Char('Reply To') default_registration_min = fields.Integer( - string='Default Minimum Registration', default=0, + 'Default Minimum Registration', default=0, help="It will select this default minimum value when you choose this event") default_registration_max = fields.Integer( - string='Default Maximum Registration', default=0, + 'Default Maximum Registration', default=0, help="It will select this default maximum value when you choose this event") @@ -54,6 +46,15 @@ class event_event(models.Model): 'event.type', string='Category', readonly=False, states={'done': [('readonly', True)]}) color = fields.Integer('Kanban Color Index') + event_mail_ids = fields.One2many('event.mail', 'event_id', string='Mail Schedule', default=lambda self: self._default_event_mail_ids()) + + @api.model + def _default_event_mail_ids(self): + return [(0, 0, { + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription') + })] # Seats and computation seats_max = fields.Integer( @@ -154,11 +155,6 @@ class event_event(models.Model): else: self.date_end_located = False - @api.one - @api.depends('address_id') - def _compute_country(self): - self.country_id = self.address_id.country_id - state = fields.Selection([ ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'), ('confirm', 'Confirmed'), ('done', 'Done')], @@ -170,23 +166,13 @@ class event_event(models.Model): def _compute_auto_confirm(self): self.auto_confirm = self.env['ir.values'].get_default('marketing.config.settings', 'auto_confirmation') - # Mailing - email_registration_id = fields.Many2one( - 'email.template', string='Registration Confirmation Email', - domain=[('model', '=', 'event.registration')], - help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.') - email_confirmation_id = fields.Many2one( - 'email.template', string='Event Confirmation Email', - domain=[('model', '=', 'event.registration')], - help="If you set an email template, each participant will receive this email announcing the confirmation of the event.") reply_to = fields.Char( - string='Reply-To Email', readonly=False, states={'done': [('readonly', True)]}, + 'Reply-To Email', 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.") address_id = fields.Many2one( 'res.partner', string='Location', default=lambda self: self.env.user.company_id.partner_id, readonly=False, states={'done': [('readonly', True)]}) - country_id = fields.Many2one('res.country', string='Country', - store=True, compute='_compute_country') + country_id = fields.Many2one('res.country', 'Country', related='address_id.country_id', store=True) description = fields.Html( string='Description', oldname='note', translate=True, readonly=False, states={'done': [('readonly', True)]}) @@ -217,7 +203,7 @@ class event_event(models.Model): def create(self, vals): res = super(event_event, self).create(vals) if res.auto_confirm: - res.confirm_event() + res.button_confirm() return res @api.one @@ -236,27 +222,16 @@ class event_event(models.Model): def button_done(self): self.state = 'done' - @api.one - def confirm_event(self): - if self.email_confirmation_id: - # send reminder that will confirm the event for all the people that were already confirmed - regs = self.registration_ids.filtered(lambda reg: reg.state not in ('draft', 'cancel')) - regs.mail_user_confirm() - self.state = 'confirm' - @api.one def button_confirm(self): - """ Confirm Event and send confirmation email to all register peoples """ - self.confirm_event() + self.state = 'confirm' @api.onchange('type') def _onchange_type(self): if self.type: - self.reply_to = self.type.default_reply_to - self.email_registration_id = self.type.default_email_registration - self.email_confirmation_id = self.type.default_email_event self.seats_min = self.type.default_registration_min self.seats_max = self.type.default_registration_max + self.reply_to = self.type.default_reply_to @api.multi def action_event_registration_report(self): @@ -267,6 +242,11 @@ class event_event(models.Model): } return res + @api.one + def mail_attendees(self, template_id, force_send=False, filter_func=lambda self: True): + for attendee in self.registration_ids.filtered(filter_func): + self.env['email.template'].browse(template_id).send_mail(attendee.id, force_send=force_send) + class event_registration(models.Model): _name = 'event.registration' @@ -283,9 +263,8 @@ class event_registration(models.Model): partner_id = fields.Many2one( 'res.partner', string='Contact', states={'done': [('readonly', True)]}) - date_open = fields.Datetime(string='Registration Date', readonly=True) + date_open = fields.Datetime(string='Registration Date', readonly=True, default=lambda self: fields.datetime.now()) # weird crash is directly now date_closed = fields.Datetime(string='Attended Date', readonly=True) - reply_to = fields.Char(string='Reply-to Email', related='event_id.reply_to', readonly=True) event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin', readonly=True) event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end', readonly=True) company_id = fields.Many2one( @@ -315,10 +294,10 @@ class event_registration(models.Model): @api.model def create(self, vals): - res = super(event_registration, self).create(vals) - if res._check_auto_confirmation()[0]: - res.sudo().confirm_registration() - return res + registration = super(event_registration, self).create(vals) + if registration._check_auto_confirmation(): + registration.sudo().confirm_registration() + return registration @api.one def do_draft(self): @@ -331,12 +310,6 @@ class event_registration(models.Model): subtype="event.mt_event_registration") self.state = 'open' - @api.one - def registration_open(self): - """ Open Registration """ - self.confirm_registration() - self.mail_user() - @api.one def button_reg_close(self): """ Close Registration """ @@ -350,23 +323,6 @@ class event_registration(models.Model): def button_reg_cancel(self): self.state = 'cancel' - @api.one - def mail_user(self): - """Send email to user with email_template when registration is done """ - if self.event_id.state == 'confirm' and self.event_id.email_confirmation_id: - self.mail_user_confirm() - else: - template = self.event_id.email_registration_id - if template: - template.send_mail(self.id) - - @api.one - def mail_user_confirm(self): - """Send email to user when the event is confirmed """ - template = self.event_id.email_confirmation_id - if template: - template.send_mail(self.id) - @api.onchange('partner_id') def _onchange_partner(self): if self.partner_id: diff --git a/addons/event/event_data.xml b/addons/event/event_data.xml index 823d6428993..2419132f409 100644 --- a/addons/event/event_data.xml +++ b/addons/event/event_data.xml @@ -1,6 +1,7 @@ + Open Event Menu @@ -21,11 +22,27 @@ - From the top menu Events, you can organize events, manage registrations, automate communication around your event and sell events through your quotations. Module Events has been installed + + + + + + [event] mail scheduler + True + + 1 + hours + -1 + + + event.mail + run + + diff --git a/addons/event/event_mail.py b/addons/event/event_mail.py new file mode 100644 index 00000000000..ea4a9a81e93 --- /dev/null +++ b/addons/event/event_mail.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +from dateutil.relativedelta import relativedelta + +from openerp import api, fields, models, tools + + +_INTERVALS = { + 'hours': lambda interval: relativedelta(hours=interval), + 'days': lambda interval: relativedelta(days=interval), + 'weeks': lambda interval: relativedelta(days=7*interval), + 'months': lambda interval: relativedelta(months=interval), + 'now': lambda interval: relativedelta(hours=0), +} + + +class EventMailScheduler(models.Model): + """ Event automated mailing. This model replaces all existing fields and + configuration allowing to send emails on events since Odoo 9. A cron exists + that periodically checks for mailing to run. """ + _name = 'event.mail' + + event_id = fields.Many2one('event.event', string='Event', required=True, readonly=True, ondelete='cascade') + interval_nbr = fields.Integer('Timing', default=1) + interval_unit = fields.Selection([ + ('now', 'Immediately'), + ('hours', 'Hour(s)'), ('days', 'Day(s)'), + ('weeks', 'Week(s)'), ('months', 'Month(s)')], + string='Unit', default='hours', required=True) + interval_type = fields.Selection([ + ('after_sub', 'After each subscription'), + ('before_event', 'Before the event'), + ('after_event', 'After the event')], + string='When to Run ', default="before_event", required=True) + template_id = fields.Many2one( + 'email.template', string='Email to Send', + domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict', + help='This field contains the template of the mail that will be automatically sent') + scheduled_date = fields.Datetime('Scheduled Sent Mail', compute='_compute_scheduled_date', store=True) + mail_registration_ids = fields.One2many('event.mail.registration', 'scheduler_id') + mail_sent = fields.Boolean('Mail Sent on Event') + done = fields.Boolean('Sent', compute='_compute_done', store=True) + + @api.one + @api.depends('mail_sent', 'interval_type', 'event_id.registration_ids', 'mail_registration_ids') + def _compute_done(self): + if self.interval_type in ['before_event', 'after_event']: + self.done = self.mail_sent + else: + self.done = len(self.mail_registration_ids) == len(self.event_id.registration_ids) and all(filter(lambda line: line.mail_sent, self.mail_registration_ids)) + + @api.one + @api.depends('event_id.state', 'event_id.date_begin', 'interval_type', 'interval_unit', 'interval_nbr') + def _compute_scheduled_date(self): + if self.event_id.state not in ['confirm', 'done']: + self.scheduled_date = False + else: + if self.interval_type == 'after_sub': + date, sign = self.event_id.create_date, 1 + elif self.interval_type == 'before_event': + date, sign = self.event_id.date_begin, -1 + else: + date, sign = self.event_id.date_end, 1 + + self.scheduled_date = datetime.strptime(date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + _INTERVALS[self.interval_unit](sign * self.interval_nbr) + + @api.one + def execute(self): + if self.interval_type == 'after_sub': + # update registration lines + lines = [] + for registration in filter(lambda item: item not in [mail_reg.registration_id for mail_reg in self.mail_registration_ids], self.event_id.registration_ids): + lines.append((0, 0, {'registration_id': registration.id})) + if lines: + self.write({'mail_registration_ids': lines}) + # execute scheduler on registrations + self.mail_registration_ids.filtered(lambda reg: reg.scheduled_date and reg.scheduled_date <= datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT)).execute() + else: + if not self.mail_sent: + self.event_id.mail_attendees(self.template_id.id) + self.write({'mail_sent': True}) + return True + + @api.model + def run(self): + schedulers = self.search([('done', '=', False), ('scheduled_date', '<=', datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))]) + return schedulers.execute() + + +class EventMailRegistration(models.Model): + _name = 'event.mail.registration' + _description = 'Registration Mail Scheduler' + _rec_name = 'scheduler_id' + _order = 'scheduled_date DESC' + + scheduler_id = fields.Many2one('event.mail', 'Mail Scheduler', required=True, ondelete='cascade') + registration_id = fields.Many2one('event.registration', 'Attendee', required=True, ondelete='cascade') + scheduled_date = fields.Datetime('Scheduled Time', compute='_compute_scheduled_date', store=True) + mail_sent = fields.Boolean('Mail Sent') + + @api.one + def execute(self): + if self.registration_id.state in ['open', 'done'] and not self.mail_sent: + self.scheduler_id.template_id.send_mail(self.registration_id.id) + self.write({'mail_sent': True}) + + @api.one + @api.depends('registration_id', 'scheduler_id.interval_unit', 'scheduler_id.interval_type') + def _compute_scheduled_date(self): + self.scheduled_date = datetime.strptime(self.registration_id.date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT) + _INTERVALS[self.scheduler_id.interval_unit](self.scheduler_id.interval_nbr) diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 9371dbe25e9..0991c4bb7af 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -16,9 +16,7 @@ - - @@ -103,12 +101,17 @@ - - - - - - + + + + + + + + + + + @@ -294,7 +297,7 @@
-