def _process_state(self, mail_values_dict): recipients_info = self._process_recipient_values(mail_values_dict) blacklist_ids = self._get_blacklist_record_ids(mail_values_dict) optout_emails = self._get_optout_emails(mail_values_dict) done_emails = self._get_done_emails(mail_values_dict) # in case of an invoice e.g. mailing_document_based = self.env.context.get('mailing_document_based') for record_id, mail_values in mail_values_dict.items(): recipients = recipients_info[record_id] # when having more than 1 recipient: we cannot really decide when a single # email is linked to several to -> skip that part. Mass mailing should # anyway always have a single recipient per record as this is default behavior. if len(recipients['mail_to']) > 1: continue mail_to = recipients['mail_to'][0] if recipients['mail_to'] else '' mail_to_normalized = recipients['mail_to_normalized'][ 0] if recipients['mail_to_normalized'] else '' # prevent sending to blocked addresses that were included by mistake # blacklisted or optout or duplicate -> cancel if record_id in blacklist_ids: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_bl' # Do not post the mail into the recipient's chatter mail_values['is_notification'] = False elif optout_emails and mail_to in optout_emails: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_optout' elif done_emails and mail_to in done_emails and not mailing_document_based: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_dup' # void of falsy values -> error elif not mail_to: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_email_missing' elif not mail_to_normalized or not email_re.findall(mail_to): mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_email_invalid' elif done_emails is not None and not mailing_document_based: done_emails.append(mail_to) return mail_values_dict
def _process_recipient_values(self, mail_values_dict): # Preprocess res.partners to batch-fetch from db if recipient_ids is present # it means they are partners (the only object to fill get_default_recipient this way) recipient_pids = [ recipient_command[1] for mail_values in mail_values_dict.values() # recipient_ids is a list of x2m command tuples at this point for recipient_command in mail_values.get('recipient_ids') or [] if recipient_command[1] ] recipient_emails = { p.id: p.email for p in self.env['res.partner'].browse(set(recipient_pids)) } if recipient_pids else {} recipients_info = {} for record_id, mail_values in mail_values_dict.items(): mail_to = [] if mail_values.get('email_to'): mail_to += email_re.findall(mail_values['email_to']) # if unrecognized email in email_to -> keep it as used for further processing if not mail_to: mail_to.append(mail_values['email_to']) # add email from recipients (res.partner) mail_to += [ recipient_emails[recipient_command[1]] for recipient_command in mail_values.get('recipient_ids') or [] if recipient_command[1] ] mail_to = list(set(mail_to)) recipients_info[record_id] = { 'mail_to': mail_to, 'mail_to_normalized': [ tools.email_normalize(mail) for mail in mail_to if tools.email_normalize(mail) ] } return recipients_info
def get_mail_values(self, res_ids): """ Override method that generated the mail content by creating the mail.mail.statistics values in the o2m of mail_mail, when doing pure email mass mailing. """ self.ensure_one() res = super(MailComposeMessage, self).get_mail_values(res_ids) # use only for allowed models in mass mailing if self.composition_mode == 'mass_mail' and \ (self.mass_mailing_name or self.mass_mailing_id) and \ self.env['ir.model'].sudo().search([('model', '=', self.model), ('is_mail_thread', '=', True)], limit=1): mass_mailing = self.mass_mailing_id if not mass_mailing: reply_to_mode = 'email' if self.no_auto_thread else 'thread' reply_to = self.reply_to if self.no_auto_thread else False mass_mailing = self.env['mail.mass_mailing'].create({ 'mass_mailing_campaign_id': self.mass_mailing_campaign_id.id, 'name': self.mass_mailing_name, 'template_id': self.template_id.id, 'state': 'done', 'reply_to_mode': reply_to_mode, 'reply_to': reply_to, 'sent_date': fields.Datetime.now(), 'body_html': self.body, 'mailing_model_id': self.env['ir.model']._get(self.model).id, 'mailing_domain': self.active_domain, }) # Preprocess res.partners to batch-fetch from db # if recipient_ids is present, it means they are partners # (the only object to fill get_default_recipient this way) recipient_partners_ids = [] read_partners = {} for res_id in res_ids: mail_values = res[res_id] if mail_values.get('recipient_ids'): # recipient_ids is a list of x2m command tuples at this point recipient_partners_ids.append( mail_values.get('recipient_ids')[0][1]) read_partners = self.env['res.partner'].browse( recipient_partners_ids) partners_email = {p.id: p.email for p in read_partners} opt_out_list = self._context.get('mass_mailing_opt_out_list') seen_list = self._context.get('mass_mailing_seen_list') mass_mail_layout = self.env.ref( 'mass_mailing.mass_mailing_mail_layout', raise_if_not_found=False) for res_id in res_ids: mail_values = res[res_id] if mail_values.get('email_to'): recips = tools.email_split(mail_values['email_to']) else: partner_id = (mail_values.get('recipient_ids') or [(False, '')])[0][1] recips = tools.email_split(partners_email.get(partner_id)) mail_to = recips[0].lower() if recips else False if (opt_out_list and mail_to in opt_out_list) or (seen_list and mail_to in seen_list) \ or (not mail_to or not email_re.findall(mail_to)): # prevent sending to blocked addresses that were included by mistake mail_values['state'] = 'cancel' elif seen_list is not None: seen_list.add(mail_to) stat_vals = { 'model': self.model, 'res_id': res_id, 'mass_mailing_id': mass_mailing.id, 'email': mail_to, } if mail_values.get('body_html') and mass_mail_layout: mail_values['body_html'] = mass_mail_layout.render( {'body': mail_values['body_html']}, engine='ir.qweb', minimal_qcontext=True) # propagate ignored state to stat when still-born if mail_values.get('state') == 'cancel': stat_vals['ignored'] = fields.Datetime.now() mail_values.update({ 'mailing_id': mass_mailing.id, 'statistics_ids': [(0, 0, stat_vals)], # email-mode: keep original message for routing 'notification': mass_mailing.reply_to_mode == 'thread', 'auto_delete': not mass_mailing.keep_archives, }) return res
def get_mail_values(self, res_ids): """ Override method that generated the mail content by creating the mail.mail.statistics values in the o2m of mail_mail, when doing pure email mass mailing. """ self.ensure_one() res = super(MailComposeMessage, self).get_mail_values(res_ids) # use only for allowed models in mass mailing if self.composition_mode == 'mass_mail' and \ (self.mass_mailing_name or self.mass_mailing_id) and \ self.env['ir.model'].sudo().search([('model', '=', self.model), ('is_mail_thread', '=', True)], limit=1): mass_mailing = self.mass_mailing_id if not mass_mailing: reply_to_mode = 'email' if self.no_auto_thread else 'thread' reply_to = self.reply_to if self.no_auto_thread else False mass_mailing = self.env['mail.mass_mailing'].create({ 'mass_mailing_campaign_id': self.mass_mailing_campaign_id.id, 'name': self.mass_mailing_name, 'template_id': self.template_id.id, 'state': 'done', 'reply_to_mode': reply_to_mode, 'reply_to': reply_to, 'sent_date': fields.Datetime.now(), 'body_html': self.body, 'mailing_model_id': self.env['ir.model']._get(self.model).id, 'mailing_domain': self.active_domain, }) # Preprocess res.partners to batch-fetch from db # if recipient_ids is present, it means they are partners # (the only object to fill get_default_recipient this way) recipient_partners_ids = [] read_partners = {} for res_id in res_ids: mail_values = res[res_id] if mail_values.get('recipient_ids'): # recipient_ids is a list of x2m command tuples at this point recipient_partners_ids.append(mail_values.get('recipient_ids')[0][1]) read_partners = self.env['res.partner'].browse(recipient_partners_ids) partners_email = {p.id: p.email for p in read_partners} opt_out_list = self._context.get('mass_mailing_opt_out_list') seen_list = self._context.get('mass_mailing_seen_list') mass_mail_layout = self.env.ref('mass_mailing.mass_mailing_mail_layout', raise_if_not_found=False) for res_id in res_ids: mail_values = res[res_id] if mail_values.get('email_to'): mail_to = tools.email_normalize(mail_values['email_to']) else: partner_id = (mail_values.get('recipient_ids') or [(False, '')])[0][1] mail_to = tools.email_normalize(partners_email.get(partner_id)) if (opt_out_list and mail_to in opt_out_list) or (seen_list and mail_to in seen_list) \ or (not mail_to or not email_re.findall(mail_to)): # prevent sending to blocked addresses that were included by mistake mail_values['state'] = 'cancel' elif seen_list is not None: seen_list.add(mail_to) stat_vals = { 'model': self.model, 'res_id': res_id, 'mass_mailing_id': mass_mailing.id, 'email': mail_to, } if mail_values.get('body_html') and mass_mail_layout: mail_values['body_html'] = mass_mail_layout.render({'body': mail_values['body_html']}, engine='ir.qweb', minimal_qcontext=True) # propagate ignored state to stat when still-born if mail_values.get('state') == 'cancel': stat_vals['ignored'] = fields.Datetime.now() mail_values.update({ 'mailing_id': mass_mailing.id, 'statistics_ids': [(0, 0, stat_vals)], # email-mode: keep original message for routing 'notification': mass_mailing.reply_to_mode == 'thread', 'auto_delete': not mass_mailing.keep_archives, }) return res