diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/event/models/event_mail.py | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/event/models/event_mail.py')
| -rw-r--r-- | addons/event/models/event_mail.py | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/addons/event/models/event_mail.py b/addons/event/models/event_mail.py new file mode 100644 index 00000000..9d9df8fa --- /dev/null +++ b/addons/event/models/event_mail.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging +import random +import threading + +from datetime import datetime +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models, tools +from odoo.tools import exception_to_unicode +from odoo.tools.translate import _ + +_logger = logging.getLogger(__name__) + +_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 EventTypeMail(models.Model): + """ Template of event.mail to attach to event.type. Those will be copied + upon all events created in that type to ease event creation. """ + _name = 'event.type.mail' + _description = 'Mail Scheduling on Event Category' + + event_type_id = fields.Many2one( + 'event.type', string='Event Type', + ondelete='cascade', required=True) + notification_type = fields.Selection([('mail', 'Mail')], string='Send', default='mail', required=True) + interval_nbr = fields.Integer('Interval', default=1) + interval_unit = fields.Selection([ + ('now', 'Immediately'), + ('hours', 'Hours'), ('days', 'Days'), + ('weeks', 'Weeks'), ('months', 'Months')], + string='Unit', default='hours', required=True) + interval_type = fields.Selection([ + ('after_sub', 'After each registration'), + ('before_event', 'Before the event'), + ('after_event', 'After the event')], + string='Trigger', default="before_event", required=True) + template_id = fields.Many2one( + 'mail.template', string='Email Template', + domain=[('model', '=', 'event.registration')], ondelete='restrict', + help='This field contains the template of the mail that will be automatically sent') + + @api.model + def _get_event_mail_fields_whitelist(self): + """ Whitelist of fields that are copied from event_type_mail_ids to event_mail_ids when + changing the event_type_id field of event.event """ + return ['notification_type', 'template_id', 'interval_nbr', 'interval_unit', 'interval_type'] + + +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' + _rec_name = 'event_id' + _description = 'Event Automated Mailing' + + event_id = fields.Many2one('event.event', string='Event', required=True, ondelete='cascade') + sequence = fields.Integer('Display order') + notification_type = fields.Selection([('mail', 'Mail')], string='Send', default='mail', required=True) + interval_nbr = fields.Integer('Interval', default=1) + interval_unit = fields.Selection([ + ('now', 'Immediately'), + ('hours', 'Hours'), ('days', 'Days'), + ('weeks', 'Weeks'), ('months', 'Months')], + string='Unit', default='hours', required=True) + interval_type = fields.Selection([ + ('after_sub', 'After each registration'), + ('before_event', 'Before the event'), + ('after_event', 'After the event')], + string='Trigger ', default="before_event", required=True) + template_id = fields.Many2one( + 'mail.template', string='Email Template', + domain=[('model', '=', 'event.registration')], 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', copy=False) + done = fields.Boolean('Sent', compute='_compute_done', store=True) + + @api.depends('mail_sent', 'interval_type', 'event_id.registration_ids', 'mail_registration_ids') + def _compute_done(self): + for mail in self: + if mail.interval_type in ['before_event', 'after_event']: + mail.done = mail.mail_sent + else: + mail.done = len(mail.mail_registration_ids) == len(mail.event_id.registration_ids) and all(mail.mail_sent for mail in mail.mail_registration_ids) + + @api.depends('event_id.date_begin', 'interval_type', 'interval_unit', 'interval_nbr') + def _compute_scheduled_date(self): + for mail in self: + if mail.interval_type == 'after_sub': + date, sign = mail.event_id.create_date, 1 + elif mail.interval_type == 'before_event': + date, sign = mail.event_id.date_begin, -1 + else: + date, sign = mail.event_id.date_end, 1 + + mail.scheduled_date = date + _INTERVALS[mail.interval_unit](sign * mail.interval_nbr) if date else False + + def execute(self): + for mail in self: + now = fields.Datetime.now() + if mail.interval_type == 'after_sub': + # update registration lines + lines = [ + (0, 0, {'registration_id': registration.id}) + for registration in (mail.event_id.registration_ids - mail.mapped('mail_registration_ids.registration_id')) + ] + if lines: + mail.write({'mail_registration_ids': lines}) + # execute scheduler on registrations + mail.mail_registration_ids.execute() + else: + # Do not send emails if the mailing was scheduled before the event but the event is over + if not mail.mail_sent and mail.scheduled_date <= now and mail.notification_type == 'mail' and \ + (mail.interval_type != 'before_event' or mail.event_id.date_end > now): + mail.event_id.mail_attendees(mail.template_id.id) + mail.write({'mail_sent': True}) + return True + + @api.model + def _warn_template_error(self, scheduler, exception): + # We warn ~ once by hour ~ instead of every 10 min if the interval unit is more than 'hours'. + if random.random() < 0.1666 or scheduler.interval_unit in ('now', 'hours'): + ex_s = exception_to_unicode(exception) + try: + event, template = scheduler.event_id, scheduler.template_id + emails = list(set([event.organizer_id.email, event.user_id.email, template.write_uid.email])) + subject = _("WARNING: Event Scheduler Error for event: %s", event.name) + body = _("""Event Scheduler for: + - Event: %(event_name)s (%(event_id)s) + - Scheduled: %(date)s + - Template: %(template_name)s (%(template_id)s) + +Failed with error: + - %(error)s + +You receive this email because you are: + - the organizer of the event, + - or the responsible of the event, + - or the last writer of the template. +""", + event_name=event.name, + event_id=event.id, + date=scheduler.scheduled_date, + template_name=template.name, + template_id=template.id, + error=ex_s) + email = self.env['ir.mail_server'].build_email( + email_from=self.env.user.email, + email_to=emails, + subject=subject, body=body, + ) + self.env['ir.mail_server'].send_email(email) + except Exception as e: + _logger.error("Exception while sending traceback by email: %s.\n Original Traceback:\n%s", e, exception) + pass + + @api.model + def run(self, autocommit=False): + schedulers = self.search([('done', '=', False), ('scheduled_date', '<=', datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))]) + for scheduler in schedulers: + try: + with self.env.cr.savepoint(): + # Prevent a mega prefetch of the registration ids of all the events of all the schedulers + self.browse(scheduler.id).execute() + except Exception as e: + _logger.exception(e) + self.invalidate_cache() + self._warn_template_error(scheduler, e) + else: + if autocommit and not getattr(threading.currentThread(), 'testing', False): + self.env.cr.commit() + return True + + +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') + + def execute(self): + now = fields.Datetime.now() + todo = self.filtered(lambda reg_mail: + not reg_mail.mail_sent and \ + reg_mail.registration_id.state in ['open', 'done'] and \ + (reg_mail.scheduled_date and reg_mail.scheduled_date <= now) and \ + reg_mail.scheduler_id.notification_type == 'mail' + ) + for reg_mail in todo: + reg_mail.scheduler_id.template_id.send_mail(reg_mail.registration_id.id) + todo.write({'mail_sent': True}) + + @api.depends('registration_id', 'scheduler_id.interval_unit', 'scheduler_id.interval_type') + def _compute_scheduled_date(self): + for mail in self: + if mail.registration_id: + date_open = mail.registration_id.date_open + date_open_datetime = date_open or fields.Datetime.now() + mail.scheduled_date = date_open_datetime + _INTERVALS[mail.scheduler_id.interval_unit](mail.scheduler_id.interval_nbr) + else: + mail.scheduled_date = False |
