def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = { 'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'customer': True, 'supplier': False, 'type': 'contact', 'website': lead.website } partner = partner.create(cr, uid, vals, context=context) return partner
def _message_find_user_id(self, cr, uid, message, context=None): from_local_part = tools.email_split(decode(message.get('From')))[0] # FP Note: canonification required, the minimu: .lower() user_ids = self.pool.get('res.users').search(cr, uid, ['|', ('login', '=', from_local_part), ('email', '=', from_local_part)], context=context) return user_ids[0] if user_ids else uid
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) email_to = [partner.email] if partner else tools.email_split(mail.email_to) return {"body": body, "body_alternative": body_alternative, "subject": subject, "email_to": email_to}
def find_or_create(self, cr, uid, email, context=None): """ Find a partner with the given ``email`` or use :py:method:`~.name_create` to create one :param str email: email-like string, which should contain at least one email, e.g. ``"Raoul Grosbedon <*****@*****.**>"``""" assert email, 'an email is required for find_or_create to work' emails = tools.email_split(email) if emails: email = emails[0] ids = self.search(cr, uid, [('email','ilike',email)], context=context) if not ids: return self.name_create(cr, uid, email, context=context)[0] return ids[0]
def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None): """ Call email_template.generate_email(), get fields relevant for mail.compose.message, transform email_cc and email_to into partner_ids """ template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) # filter template values fields = ['body', 'body_html', 'subject', 'email_to', 'email_cc', 'attachments'] values = dict((field, template_values[field]) for field in fields if template_values.get(field)) values['body'] = values.pop('body_html', '') # transform email_to, email_cc into partner_ids values['partner_ids'] = [] mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', '')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) values['partner_ids'].append(partner_id) return values
def onchange_email(self, cr, uid, ids, email, context=None): context = context or {} warning = {} if not email: return {} if not tools.email_split(email): warning = { 'title': _('Email Format Warning!'), 'message': _('The email is not well formatted, it should be [email protected]' ) } return { 'warning': warning, }
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = { 'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact' } partner = partner.create(cr, uid,vals, context) return partner
def do_action(self, cr, uid, action, model_obj, obj, context=None): """ Do Action @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param action: pass action @param model_obj: pass Model object @param context: A standard dictionary for contextual values """ if context is None: context = {} if action.server_action_id: context.update({'active_id':obj.id, 'active_ids':[obj.id]}) self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) write = {} if hasattr(obj, 'user_id') and action.act_user_id: obj.user_id = action.act_user_id write['user_id'] = action.act_user_id.id if hasattr(obj, 'date_action_last'): write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S') if hasattr(obj, 'state') and action.act_state: obj.state = action.act_state write['state'] = action.act_state if hasattr(obj, 'categ_id') and action.act_categ_id: obj.categ_id = action.act_categ_id write['categ_id'] = action.act_categ_id.id model_obj.write(cr, uid, [obj.id], write, context) if hasattr(model_obj, 'remind_user') and action.act_remind_user: model_obj.remind_user(cr, uid, [obj.id], context, attach=action.act_remind_attach) if hasattr(model_obj, 'remind_partner') and action.act_remind_partner: model_obj.remind_partner(cr, uid, [obj.id], context, attach=action.act_remind_attach) if action.act_method: getattr(model_obj, 'act_method')(cr, uid, [obj.id], action, context) emails = [] if hasattr(obj, 'user_id') and action.act_mail_to_user: if obj.user_id: emails.append(obj.user_id.email) if action.act_mail_to_watchers: emails += (action.act_email_cc or '').split(',') if action.act_mail_to_email: emails += (action.act_mail_to_email or '').split(',') locals_for_emails = { 'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context), 'obj' : obj, } if action.act_email_to: emails.append(safe_eval(action.act_email_to, {}, locals_for_emails)) emails = filter(None, emails) if len(emails) and action.act_mail_body: emails = list(set(emails)) email_from = safe_eval(action.act_email_from, {}, locals_for_emails) emails = tools.email_split(','.join(filter(None, emails))) email_froms = tools.email_split(email_from) if email_froms: self.email_send(cr, uid, obj, emails, action.act_mail_body, emailfrom=email_froms[0]) return True
# -*- coding: utf-8 -*-
def message_route(self, cr, uid, message, model=None, thread_id=None, custom_values=None, context=None): """Attempt to figure out the correct target model, thread_id, custom_values and user_id to use for an incoming message. Multiple values may be returned, if a message had multiple recipients matching existing mail.aliases, for example. The following heuristics are used, in this order: 1. If the message replies to an existing thread_id, and properly contains the thread model in the 'In-Reply-To' header, use this model/thread_id pair, and ignore custom_value (not needed as no creation will take place) 2. Look for a mail.alias entry matching the message recipient, and use the corresponding model, thread_id, custom_values and user_id. 3. Fallback to the ``model``, ``thread_id`` and ``custom_values`` provided. 4. If all the above fails, raise an exception. :param string message: an email.message instance :param string model: the fallback model to use if the message does not match any of the currently configured mail aliases (may be None if a matching alias is supposed to be present) :type dict custom_values: optional dictionary of default field values to pass to ``message_new`` if a new record needs to be created. Ignored if the thread record already exists, and also if a matching mail.alias was found (aliases define their own defaults) :param int thread_id: optional ID of the record/thread from ``model`` to which this mail should be attached. Only used if the message does not reply to an existing thread and does not match any mail alias. :return: list of [model, thread_id, custom_values, user_id] """ assert isinstance(message, Message), 'message must be an email.message.Message at this point' message_id = message.get('Message-Id') # 1. Verify if this is a reply to an existing thread references = decode_header(message, 'References') or decode_header(message, 'In-Reply-To') ref_match = references and tools.reference_re.search(references) if ref_match: thread_id = int(ref_match.group(1)) model = ref_match.group(2) or model model_pool = self.pool.get(model) if thread_id and model and model_pool and model_pool.exists(cr, uid, thread_id) \ and hasattr(model_pool, 'message_update'): _logger.debug('Routing mail with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s', message_id, model, thread_id, custom_values, uid) return [(model, thread_id, custom_values, uid)] # 2. Look for a matching mail.alias entry # Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values # for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value. rcpt_tos = decode_header(message, 'Delivered-To') or \ ','.join([decode_header(message, 'To'), decode_header(message, 'Cc'), decode_header(message, 'Resent-To'), decode_header(message, 'Resent-Cc')]) local_parts = [e.split('@')[0] for e in tools.email_split(rcpt_tos)] if local_parts: mail_alias = self.pool.get('mail.alias') alias_ids = mail_alias.search(cr, uid, [('alias_name', 'in', local_parts)]) if alias_ids: routes = [] for alias in mail_alias.browse(cr, uid, alias_ids, context=context): user_id = alias.alias_user_id.id if not user_id: user_id = self._message_find_user_id(cr, uid, message, context=context) routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, \ eval(alias.alias_defaults), user_id)) _logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes) return routes # 3. Fallback to the provided parameters, if they work model_pool = self.pool.get(model) if not thread_id: # Legacy: fallback to matching [ID] in the Subject match = tools.res_re.search(decode_header(message, 'Subject')) thread_id = match and match.group(1) assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \ "No possible route found for incoming message with Message-Id %s. " \ "Create an appropriate mail.alias or force the destination model." % message_id if thread_id and not model_pool.exists(cr, uid, thread_id): _logger.warning('Received mail reply to missing document %s! Ignoring and creating new document instead for Message-Id %s', thread_id, message_id) thread_id = None _logger.debug('Routing mail with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s', message_id, model, thread_id, custom_values, uid) return [(model, thread_id, custom_values, uid)]
def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None): """ Find partners related to some header fields of the message. """ s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)]) return [partner_id for email in tools.email_split(s) for partner_id in self.pool.get('res.partner').search(cr, uid, [('email', 'ilike', email)], context=context)]
def send(self, cr, uid, ids, auto_commit=False, recipient_ids=None, context=None): """ Sends the selected emails immediately, ignoring their current state (mails that have already been sent should not be passed unless they should actually be re-sent). Emails successfully delivered are marked as 'sent', and those that fail to be deliver are marked as 'exception', and the corresponding error mail is output in the server logs. :param bool auto_commit: whether to force a commit of the mail status after sending each mail (meant only for scheduler processing); should never be True during normal transactions (default: False) :param list recipient_ids: specific list of res.partner recipients. If set, one email is sent to each partner. Its is possible to tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``. If not specified, one email is sent to mail_mail.email_to. :return: True """ ir_mail_server = self.pool.get("ir.mail_server") for mail in self.browse(cr, uid, ids, context=context): try: # handle attachments attachments = [] for attach in mail.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) # specific behavior to customize the send email for notified partners email_list = [] if recipient_ids: for partner in self.pool.get("res.partner").browse(cr, uid, recipient_ids, context=context): email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) else: email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) # build an RFC2822 email.message.Message object and send it without queuing for email in email_list: msg = ir_mail_server.build_email( email_from=mail.email_from, email_to=email.get("email_to"), subject=email.get("subject"), body=email.get("body"), body_alternative=email.get("body_alternative"), email_cc=tools.email_split(mail.email_cc), reply_to=mail.reply_to, attachments=attachments, message_id=mail.message_id, references=mail.references, object_id=mail.res_id and ("%s-%s" % (mail.res_id, mail.model)), subtype="html", subtype_alternative="plain", ) res = ir_mail_server.send_email( cr, uid, msg, mail_server_id=mail.mail_server_id.id, context=context ) if res: mail.write({"state": "sent", "message_id": res}) else: mail.write({"state": "exception"}) mail.refresh() if mail.state == "sent": self._postprocess_sent_message(cr, uid, mail, context=context) except Exception: _logger.exception("failed sending mail.mail %s", mail.id) mail.write({"state": "exception"}) if auto_commit == True: cr.commit() return True