Ejemplo n.º 1
0
 def phone_get_sanitized_numbers(self, number_fname='mobile', force_format='E164'):
     res = dict.fromkeys(self.ids, False)
     country_fname = self._phone_get_country_field()
     for record in self:
         number = record[number_fname]
         res[record.id] = phone_validation.phone_sanitize_numbers_w_record([number], record, record_country_fname=country_fname, force_format=force_format)[number]['sanitized']
     return res
Ejemplo n.º 2
0
 def _compute_sanitized_numbers(self):
     for composer in self:
         if composer.numbers:
             record = composer._get_records(
             ) if composer.res_model and composer.res_id else self.env.user
             numbers = [
                 number.strip() for number in composer.numbers.split(',')
             ]
             sanitize_res = phone_validation.phone_sanitize_numbers_w_record(
                 numbers, record)
             sanitized_numbers = [
                 info['sanitized'] for info in sanitize_res.values()
                 if info['sanitized']
             ]
             invalid_numbers = [
                 number for number, info in sanitize_res.items()
                 if info['code']
             ]
             if invalid_numbers:
                 raise UserError(
                     _('Following numbers are not correctly encoded: %s') %
                     repr(invalid_numbers))
             composer.sanitized_numbers = ','.join(sanitized_numbers)
         else:
             composer.sanitized_numbers = False
    def create(self, values):
        # First of all, extract values to ensure emails are really unique (and don't modify values in place)
        to_create = []
        done = set()
        for value in values:
            number = value['number']
            sanitized_values = phone_validation.phone_sanitize_numbers_w_record(
                [number], self.env.user)[number]
            sanitized = sanitized_values['sanitized']
            if not sanitized:
                raise UserError(sanitized_values['msg'] +
                                _(" Please correct the number and try again."))
            if sanitized in done:
                continue
            done.add(sanitized)
            to_create.append(dict(value, number=sanitized))
        """ To avoid crash during import due to unique email, return the existing records if any """
        sql = '''SELECT number, id FROM phone_blacklist WHERE number = ANY(%s)'''
        numbers = [v['number'] for v in to_create]
        self._cr.execute(sql, (numbers, ))
        bl_entries = dict(self._cr.fetchall())
        to_create = [v for v in to_create if v['number'] not in bl_entries]

        results = super(PhoneBlackList, self).create(to_create)
        return self.env['phone.blacklist'].browse(
            bl_entries.values()) | results
Ejemplo n.º 4
0
 def _search(self,
             args,
             offset=0,
             limit=None,
             order=None,
             count=False,
             access_rights_uid=None):
     """ Override _search in order to grep search on sanitized number field """
     if args:
         new_args = []
         for arg in args:
             if isinstance(
                     arg,
                 (list, tuple)) and arg[0] == 'number' and isinstance(
                     arg[2], str):
                 number = arg[2]
                 sanitized = phone_validation.phone_sanitize_numbers_w_record(
                     [number], self.env.user)[number]['sanitized']
                 if sanitized:
                     new_args.append([arg[0], arg[1], sanitized])
                 else:
                     new_args.append(arg)
             else:
                 new_args.append(arg)
     else:
         new_args = args
     return super(PhoneBlackList,
                  self)._search(new_args,
                                offset=offset,
                                limit=limit,
                                order=order,
                                count=count,
                                access_rights_uid=access_rights_uid)
Ejemplo n.º 5
0
 def action_send_sms(self):
     self.ensure_one()
     numbers = [number.strip() for number in self.numbers.split(',')]
     sanitize_res = phone_validation.phone_sanitize_numbers_w_record(
         numbers, self.env.user)
     sanitized_numbers = [
         info['sanitized'] for info in sanitize_res.values()
         if info['sanitized']
     ]
     invalid_numbers = [
         number for number, info in sanitize_res.items() if info['code']
     ]
     if invalid_numbers:
         raise exceptions.UserError(
             _('Following numbers are not correctly encoded: %s, example : "+32 495 85 85 77, +33 545 55 55 55"'
               ) % repr(invalid_numbers))
     self.env['sms.api']._send_sms_batch([{
         'res_id':
         0,
         'number':
         number,
         'content':
         self.mailing_id.body_plaintext,
     } for number in sanitized_numbers])
     return True
Ejemplo n.º 6
0
 def _compute_recipient_single_valid(self):
     for composer in self:
         value = composer.recipient_single_number_itf or composer.recipient_single_number
         if value:
             records = composer._get_records()
             sanitized = phone_validation.phone_sanitize_numbers_w_record([value], records)[value]['sanitized']
             composer.recipient_single_valid = bool(sanitized)
         else:
             composer.recipient_single_valid = False
Ejemplo n.º 7
0
 def write(self, values):
     if 'number' in values:
         number = values['number']
         sanitized = phone_validation.phone_sanitize_numbers_w_record(
             [number], self.env.user)[number]['sanitized']
         if not sanitized:
             raise UserError(_('Invalid number %s') % number)
         values['number'] = sanitized
     return super(PhoneBlackList, self).write(values)
Ejemplo n.º 8
0
 def write(self, values):
     if 'number' in values:
         number = values['number']
         sanitized_values = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]
         sanitized = sanitized_values['sanitized']
         if not sanitized:
             raise UserError(sanitized_values['msg'] + _(" Please correct the number and try again."))
         values['number'] = sanitized
     return super(PhoneBlackList, self).write(values)
Ejemplo n.º 9
0
 def phone_get_sanitized_number(self,
                                number_fname='mobile',
                                force_format='E164'):
     self.ensure_one()
     country_fname = self._phone_get_country_field()
     number = self[number_fname]
     return phone_validation.phone_sanitize_numbers_w_record(
         [number],
         self,
         record_country_fname=country_fname,
         force_format=force_format)[number]['sanitized']
Ejemplo n.º 10
0
def valid_alternative_9number(info):
    """Validar um numero alternativo adicionando um numero 9"""
    if not info.get("number"):
        return False

    alternative_number = get_number_e164(info.get("number"))

    valid_number = phone_validation.phone_sanitize_numbers_w_record(
        [alternative_number], info.get("partner"))

    if valid_number.get(alternative_number).get("sanitized"):
        return valid_number.get(alternative_number).get("sanitized")
    return False
Ejemplo n.º 11
0
 def phone_get_sanitized_number(self,
                                number_fname='mobile',
                                force_format='E164'):
     """ Stand alone version, allowing to use it on partner model without
     having any dependency on sms module. To cleanup in master (15.3 +)."""
     self.ensure_one()
     country_fname = 'country_id'
     number = self[number_fname]
     return phone_validation.phone_sanitize_numbers_w_record(
         [number],
         self,
         record_country_fname=country_fname,
         force_format=force_format)[number]['sanitized']
Ejemplo n.º 12
0
    def action_send_sms(self):
        self.ensure_one()

        numbers = [number.strip() for number in self.numbers.splitlines()]
        sanitize_res = phone_validation.phone_sanitize_numbers_w_record(numbers, self.env.user)
        sanitized_numbers = [info['sanitized'] for info in sanitize_res.values() if info['sanitized']]
        invalid_numbers = [number for number, info in sanitize_res.items() if info['code']]

        record = self.env[self.mailing_id.mailing_model_real].search([], limit=1)
        body = self.mailing_id.body_plaintext
        if record:
            # Returns a proper error if there is a syntax error with jinja
            body = self.env['mail.render.mixin']._render_template(body, self.mailing_id.mailing_model_real, record.ids)[record.id]

        # res_id is used to map the result to the number to log notifications as IAP does not return numbers...
        # TODO: clean IAP to make it return a clean dict with numbers / use custom keys / rename res_id to external_id
        sent_sms_list = self.env['sms.api']._send_sms_batch([{
            'res_id': number,
            'number': number,
            'content': body,
        } for number in sanitized_numbers])

        error_messages = {}
        if any(sent_sms.get('state') != 'success' for sent_sms in sent_sms_list):
            error_messages = self.env['sms.api']._get_sms_api_error_messages()

        notification_messages = []
        if invalid_numbers:
            notification_messages.append(_('The following numbers are not correctly encoded: %s',
                ', '.join(invalid_numbers)))

        for sent_sms in sent_sms_list:
            if sent_sms.get('state') == 'success':
                notification_messages.append(
                    _('Test SMS successfully sent to %s', sent_sms.get('res_id')))
            elif sent_sms.get('state'):
                notification_messages.append(
                    _('Test SMS could not be sent to %s:<br>%s',
                    sent_sms.get('res_id'),
                    error_messages.get(sent_sms['state'], _("An error occurred.")))
                )

        if notification_messages:
            self.mailing_id._message_log(body='<ul>%s</ul>' % ''.join(
                ['<li>%s</li>' % notification_message for notification_message in notification_messages]
            ))

        return True
Ejemplo n.º 13
0
 def _compute_sanitized_numbers(self):
     if self.numbers:
         numbers = [number.strip() for number in self.numbers.split(',')]
         sanitize_res = phone_validation.phone_sanitize_numbers_w_record(
             numbers, self.env.user)
         sanitized_numbers = [
             info['sanitized'] for info in sanitize_res.values()
             if info['sanitized']
         ]
         invalid_numbers = [
             number for number, info in sanitize_res.items() if info['code']
         ]
         if invalid_numbers:
             raise exceptions.UserError(
                 _('Following numbers are not correctly encoded: %s') %
                 repr(invalid_numbers))
         self.sanitized_numbers = ','.join(sanitized_numbers)
     else:
         self.sanitized_numbers = False
    def action_send_sms(self):
        self.ensure_one()
        numbers = [number.strip() for number in self.numbers.split(',')]
        sanitize_res = phone_validation.phone_sanitize_numbers_w_record(numbers, self.env.user)
        sanitized_numbers = [info['sanitized'] for info in sanitize_res.values() if info['sanitized']]
        invalid_numbers = [number for number, info in sanitize_res.items() if info['code']]
        if invalid_numbers:
            raise exceptions.UserError(_('Following numbers are not correctly encoded: %s, example : "+32 495 85 85 77, +33 545 55 55 55"', repr(invalid_numbers)))
        
        record = self.env[self.mailing_id.mailing_model_real].search([], limit=1)
        body = self.mailing_id.body_plaintext
        if record:
            # Returns a proper error if there is a syntax error with jinja
            body = self.env['mail.render.mixin']._render_template(body, self.mailing_id.mailing_model_real, record.ids)[record.id]

        self.env['sms.api']._send_sms_batch([{
            'res_id': 0,
            'number': number,
            'content': body,
        } for number in sanitized_numbers])
        return True
Ejemplo n.º 15
0
    def phone_get_sanitized_number(self,
                                   number_fname='mobile',
                                   force_format='E164'):

        sanitized = super(PhoneMixin, self).phone_get_sanitized_number(
            number_fname='mobile', force_format='E164')

        if sanitized:
            return sanitized

        country_fname = self._phone_get_country_field()
        number = self[number_fname]
        # Valida numero alternativo adicionando um 9 e caracteres de controle
        alternative_number = helpers.get_number_e164(number)
        sanitized = phone_validation.phone_sanitize_numbers_w_record(
            [alternative_number],
            self,
            record_country_fname=country_fname,
            force_format=force_format)[alternative_number]['sanitized']
        if sanitized:
            return number

        return False
Ejemplo n.º 16
0
    def _sms_get_recipients_info(self,
                                 force_field=False,
                                 partner_fallback=True):
        """" Get SMS recipient information on current record set. This method
        checks for numbers and sanitation in order to centralize computation.

        Example of use cases

          * click on a field -> number is actually forced from field, find customer
            linked to record, force its number to field or fallback on customer fields;
          * contact -> find numbers from all possible phone fields on record, find
            customer, force its number to found field number or fallback on customer fields;

        :param force_field: either give a specific field to find phone number, either
            generic heuristic is used to find one based on ``_sms_get_number_fields``;
        :param partner_fallback: if no value found in the record, check its customer
            values based on ``_sms_get_default_partners``;

        :return dict: record.id: {
            'partner': a res.partner recordset that is the customer (void or singleton)
                linked to the recipient. See ``_sms_get_default_partners``;
            'sanitized': sanitized number to use (coming from record's field or partner's
                phone fields). Set to False is number impossible to parse and format;
            'number': original number before sanitation;
            'partner_store': whether the number comes from the customer phone fields. If
                False it means number comes from the record itself, even if linked to a
                customer;
            'field_store': field in which the number has been found (generally mobile or
                phone, see ``_sms_get_number_fields``);
        } for each record in self
        """
        result = dict.fromkeys(self.ids, False)
        tocheck_fields = [force_field
                          ] if force_field else self._sms_get_number_fields()
        for record in self:
            all_numbers = [
                record[fname] for fname in tocheck_fields if fname in record
            ]
            all_partners = record._sms_get_default_partners()

            valid_number = False
            for fname in [f for f in tocheck_fields if f in record]:
                valid_number = phone_validation.phone_sanitize_numbers_w_record(
                    [record[fname]], record)[record[fname]]['sanitized']
                if valid_number:
                    break

            if valid_number:
                result[record.id] = {
                    'partner':
                    all_partners[0]
                    if all_partners else self.env['res.partner'],
                    'sanitized':
                    valid_number,
                    'number':
                    record[fname],
                    'partner_store':
                    False,
                    'field_store':
                    fname,
                }
            elif all_partners and partner_fallback:
                partner = self.env['res.partner']
                for partner in all_partners:
                    for fname in self.env[
                            'res.partner']._sms_get_number_fields():
                        valid_number = phone_validation.phone_sanitize_numbers_w_record(
                            [partner[fname]],
                            record)[partner[fname]]['sanitized']
                        if valid_number:
                            break

                if not valid_number:
                    fname = 'mobile' if partner.mobile else (
                        'phone' if partner.phone else 'mobile')

                result[record.id] = {
                    'partner': partner,
                    'sanitized': valid_number if valid_number else False,
                    'number': partner[fname],
                    'partner_store': True,
                    'field_store': fname,
                }
            else:
                # did not find any sanitized number -> take first set value as fallback;
                # if none, just assign False to the first available number field
                value, fname = next(
                    ((value, fname)
                     for value, fname in zip(all_numbers, tocheck_fields)
                     if value),
                    (False, tocheck_fields[0] if tocheck_fields else False))
                result[record.id] = {
                    'partner': self.env['res.partner'],
                    'sanitized': False,
                    'number': value,
                    'partner_store': False,
                    'field_store': fname
                }
        return result
Ejemplo n.º 17
0
    def _notify_record_by_sms(self,
                              message,
                              recipients_data,
                              msg_vals=False,
                              sms_numbers=None,
                              sms_pid_to_number=None,
                              check_existing=False,
                              put_in_queue=False,
                              **kwargs):
        """ Notification method: by SMS.

        :param message: mail.message record to notify;
        :param recipients_data: see ``_notify_thread``;
        :param msg_vals: see ``_notify_thread``;

        :param sms_numbers: additional numbers to notify in addition to partners
          and classic recipients;
        :param pid_to_number: force a number to notify for a given partner ID
              instead of taking its mobile / phone number;
        :param check_existing: check for existing notifications to update based on
          mailed recipient, otherwise create new notifications;
        :param put_in_queue: use cron to send queued SMS instead of sending them
          directly;
        """
        sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {}
        sms_numbers = sms_numbers if sms_numbers is not None else []
        sms_create_vals = []
        sms_all = self.env['sms.sms'].sudo()

        # pre-compute SMS data
        body = msg_vals['body'] if msg_vals and msg_vals.get(
            'body') else message.body
        sms_base_vals = {
            'body': html2plaintext(body),
            'mail_message_id': message.id,
            'state': 'outgoing',
        }

        # notify from computed recipients_data (followers, specific recipients)
        partners_data = [
            r for r in recipients_data['partners'] if r['notif'] == 'sms'
        ]
        partner_ids = [r['id'] for r in partners_data]
        if partner_ids:
            for partner in self.env['res.partner'].sudo().browse(partner_ids):
                number = sms_pid_to_number.get(
                    partner.id) or partner.mobile or partner.phone
                sanitize_res = phone_validation.phone_sanitize_numbers_w_record(
                    [number], partner)[number]
                number = sanitize_res['sanitized'] or number
                sms_create_vals.append(
                    dict(sms_base_vals, partner_id=partner.id, number=number))

        # notify from additional numbers
        if sms_numbers:
            sanitized = phone_validation.phone_sanitize_numbers_w_record(
                sms_numbers, self)
            tocreate_numbers = [
                value['sanitized'] or original
                for original, value in sanitized.items()
            ]
            sms_create_vals += [
                dict(
                    sms_base_vals,
                    partner_id=False,
                    number=n,
                    state='outgoing' if n else 'error',
                    error_code='' if n else 'sms_number_missing',
                ) for n in tocreate_numbers
            ]

        # create sms and notification
        existing_pids, existing_numbers = [], []
        if sms_create_vals:
            sms_all |= self.env['sms.sms'].sudo().create(sms_create_vals)

            if check_existing:
                existing = self.env['mail.notification'].sudo().search([
                    '|', ('res_partner_id', 'in', partner_ids), '&',
                    ('res_partner_id', '=', False),
                    ('sms_number', 'in', sms_numbers),
                    ('notification_type', '=', 'sms'),
                    ('mail_message_id', '=', message.id)
                ])
                for n in existing:
                    if n.res_partner_id.id in partner_ids and n.mail_message_id == message:
                        existing_pids.append(n.res_partner_id.id)
                    if not n.res_partner_id and n.sms_number in sms_numbers and n.mail_message_id == message:
                        existing_numbers.append(n.sms_number)

            notif_create_values = [
                {
                    'mail_message_id':
                    message.id,
                    'res_partner_id':
                    sms.partner_id.id,
                    'sms_number':
                    sms.number,
                    'notification_type':
                    'sms',
                    'sms_id':
                    sms.id,
                    'is_read':
                    True,  # discard Inbox notification
                    'notification_status':
                    'ready' if sms.state == 'outgoing' else 'exception',
                    'failure_type':
                    '' if sms.state == 'outgoing' else sms.error_code,
                } for sms in sms_all
                if (sms.partner_id and sms.partner_id.id not in existing_pids)
                or (not sms.partner_id and sms.number not in existing_numbers)
            ]
            if notif_create_values:
                self.env['mail.notification'].sudo().create(
                    notif_create_values)

            if existing_pids or existing_numbers:
                for sms in sms_all:
                    notif = next(
                        (n for n in existing
                         if (n.res_partner_id.id in existing_pids
                             and n.res_partner_id.id == sms.partner_id.id) or
                         (not n.res_partner_id and n.sms_number in
                          existing_numbers and n.sms_number == sms.number)),
                        False)
                    if notif:
                        notif.write({
                            'notification_type': 'sms',
                            'notification_status': 'ready',
                            'sms_id': sms.id,
                            'sms_number': sms.number,
                        })

        if sms_all and not put_in_queue:
            sms_all.filtered(lambda sms: sms.state == 'outgoing').send(
                auto_commit=False, raise_exception=False)

        return True
Ejemplo n.º 18
0
 def add(self, number):
     sanitized = phone_validation.phone_sanitize_numbers_w_record(
         [number], self.env.user)[number]['sanitized']
     return self._add([sanitized])
Ejemplo n.º 19
0
    def _sms_get_recipients_info(self, force_field=False):
        """" Get SMS recipient information on current record set. This method
        checks for numbers and sanitation in order to centralize computation.

        Example of use cases

          * click on a field -> number is actually forced from field, find customer
            linked to record, force its number to field or fallback on customer fields;
          * contact -> find numbers from all possible phone fields on record, find
            customer, force its number to found field number or fallback on customer fields;

        :return dict: record.id: {
            'partner': a res.partner recordset that is the customer (void or singleton);
            'sanitized': sanitized number to use (coming from record's field or partner's mobile
              or phone). Set to False is number impossible to parse and format;
            'number': original number before sanitation;
        } for each record in self
        """
        result = dict.fromkeys(self.ids, False)
        number_fields = self._sms_get_number_fields()
        for record in self:
            tocheck_fields = [force_field] if force_field else number_fields
            all_numbers = [
                record[fname] for fname in tocheck_fields if fname in record
            ]
            all_partners = record._sms_get_default_partners()

            valid_number = False
            for fname in [f for f in tocheck_fields if f in record]:
                valid_number = phone_validation.phone_sanitize_numbers_w_record(
                    [record[fname]], record)[record[fname]]['sanitized']
                if valid_number:
                    break

            if valid_number:
                result[record.id] = {
                    'partner':
                    all_partners[0]
                    if all_partners else self.env['res.partner'],
                    'sanitized':
                    valid_number,
                    'number':
                    valid_number,
                }
            elif all_partners:
                partner_number, partner = False, self.env['res.partner']
                for partner in all_partners:
                    partner_number = partner.mobile or partner.phone
                    if partner_number:
                        partner_number = phone_validation.phone_sanitize_numbers_w_record(
                            [partner_number],
                            record)[partner_number]['sanitized']
                    if partner_number:
                        break

                if partner_number:
                    result[record.id] = {
                        'partner': partner,
                        'sanitized': partner_number,
                        'number': partner_number
                    }
                else:
                    result[record.id] = {
                        'partner': partner,
                        'sanitized': False,
                        'number': partner.mobile or partner.phone
                    }
            elif all_numbers:
                result[record.id] = {
                    'partner': self.env['res.partner'],
                    'sanitized': False,
                    'number': all_numbers[0]
                }
            else:
                result[record.id] = {
                    'partner': self.env['res.partner'],
                    'sanitized': False,
                    'number': False
                }
        return result
Ejemplo n.º 20
0
    def _notify_thread_by_sms(self,
                              message,
                              recipients_data,
                              msg_vals=False,
                              sms_numbers=None,
                              sms_pid_to_number=None,
                              check_existing=False,
                              put_in_queue=False,
                              **kwargs):
        """ Notification method: by SMS.

        :param message: ``mail.message`` record to notify;
        :param recipients_data: list of recipients information (based on res.partner
          records), formatted like
            [{'active': partner.active;
              'id': id of the res.partner being recipient to notify;
              'groups': res.group IDs if linked to a user;
              'notif': 'inbox', 'email', 'sms' (SMS App);
              'share': partner.partner_share;
              'type': 'customer', 'portal', 'user;'
             }, {...}].
          See ``MailThread._notify_get_recipients``;
        :param msg_vals: dictionary of values used to create the message. If given it
          may be used to access values related to ``message`` without accessing it
          directly. It lessens query count in some optimized use cases by avoiding
          access message content in db;

        :param sms_numbers: additional numbers to notify in addition to partners
          and classic recipients;
        :param pid_to_number: force a number to notify for a given partner ID
              instead of taking its mobile / phone number;
        :param check_existing: check for existing notifications to update based on
          mailed recipient, otherwise create new notifications;
        :param put_in_queue: use cron to send queued SMS instead of sending them
          directly;
        """
        sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {}
        sms_numbers = sms_numbers if sms_numbers is not None else []
        sms_create_vals = []
        sms_all = self.env['sms.sms'].sudo()

        # pre-compute SMS data
        body = msg_vals[
            'body'] if msg_vals and 'body' in msg_vals else message.body
        sms_base_vals = {
            'body': html2plaintext(body),
            'mail_message_id': message.id,
            'state': 'outgoing',
        }

        # notify from computed recipients_data (followers, specific recipients)
        partners_data = [r for r in recipients_data if r['notif'] == 'sms']
        partner_ids = [r['id'] for r in partners_data]
        if partner_ids:
            for partner in self.env['res.partner'].sudo().browse(partner_ids):
                number = sms_pid_to_number.get(
                    partner.id) or partner.mobile or partner.phone
                sanitize_res = phone_validation.phone_sanitize_numbers_w_record(
                    [number], partner)[number]
                number = sanitize_res['sanitized'] or number
                sms_create_vals.append(
                    dict(sms_base_vals, partner_id=partner.id, number=number))

        # notify from additional numbers
        if sms_numbers:
            sanitized = phone_validation.phone_sanitize_numbers_w_record(
                sms_numbers, self)
            tocreate_numbers = [
                value['sanitized'] or original
                for original, value in sanitized.items()
            ]
            sms_create_vals += [
                dict(
                    sms_base_vals,
                    partner_id=False,
                    number=n,
                    state='outgoing' if n else 'error',
                    failure_type='' if n else 'sms_number_missing',
                ) for n in tocreate_numbers
            ]

        # create sms and notification
        existing_pids, existing_numbers = [], []
        if sms_create_vals:
            sms_all |= self.env['sms.sms'].sudo().create(sms_create_vals)

            if check_existing:
                existing = self.env['mail.notification'].sudo().search([
                    '|', ('res_partner_id', 'in', partner_ids), '&',
                    ('res_partner_id', '=', False),
                    ('sms_number', 'in', sms_numbers),
                    ('notification_type', '=', 'sms'),
                    ('mail_message_id', '=', message.id)
                ])
                for n in existing:
                    if n.res_partner_id.id in partner_ids and n.mail_message_id == message:
                        existing_pids.append(n.res_partner_id.id)
                    if not n.res_partner_id and n.sms_number in sms_numbers and n.mail_message_id == message:
                        existing_numbers.append(n.sms_number)

            notif_create_values = [
                {
                    'author_id':
                    message.author_id.id,
                    'mail_message_id':
                    message.id,
                    'res_partner_id':
                    sms.partner_id.id,
                    'sms_number':
                    sms.number,
                    'notification_type':
                    'sms',
                    'sms_id':
                    sms.id,
                    'is_read':
                    True,  # discard Inbox notification
                    'notification_status':
                    'ready' if sms.state == 'outgoing' else 'exception',
                    'failure_type':
                    '' if sms.state == 'outgoing' else sms.failure_type,
                } for sms in sms_all
                if (sms.partner_id and sms.partner_id.id not in existing_pids)
                or (not sms.partner_id and sms.number not in existing_numbers)
            ]
            if notif_create_values:
                self.env['mail.notification'].sudo().create(
                    notif_create_values)

            if existing_pids or existing_numbers:
                for sms in sms_all:
                    notif = next(
                        (n for n in existing
                         if (n.res_partner_id.id in existing_pids
                             and n.res_partner_id.id == sms.partner_id.id) or
                         (not n.res_partner_id and n.sms_number in
                          existing_numbers and n.sms_number == sms.number)),
                        False)
                    if notif:
                        notif.write({
                            'notification_type': 'sms',
                            'notification_status': 'ready',
                            'sms_id': sms.id,
                            'sms_number': sms.number,
                        })

        if sms_all and not put_in_queue:
            sms_all.filtered(lambda sms: sms.state == 'outgoing').send(
                auto_commit=False, raise_exception=False)

        return True