summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorimond Husquinet (fhu) <fhu@odoo.com>2019-11-15 13:21:02 +0000
committerThibault Delavallée <tde@odoo.com>2020-03-05 11:27:01 +0000
commitf685e70616e8fe357690cd8e6cfa4543c1d0df24 (patch)
tree6b818d3e3ae89d0ba9de2530d24296145883a3ca
parente36661ecd9fc455222f4d274eb65ab763a6ccd18 (diff)
[IMP] sms: allow to edit number when sending an SMS to one recipient
After this commit * the SMS button is visible next to a phone number in mass_mailing, and mass_mailing_sms * when sending an sms to a single contact, its phone number is now an editable field in the wizard and there is a warning if the number is invalid; * if operator updated the recipient number, update record number according to the new number only in single recipient mode (aka, solving number issues directly from interface); LINKS Task ID 2088303 Community PR odoo/odoo#40482 Upgrade PR odoo/upgrade#873 Co-Authored-By: Florimond Husquinet <fhu@odoo.com> Co-Authored-By: Thibault Delavallée <tde@odoo.com>
-rw-r--r--addons/sms/models/mail_thread.py5
-rw-r--r--addons/sms/wizard/sms_composer.py92
-rw-r--r--addons/sms/wizard/sms_composer_views.xml23
-rw-r--r--addons/test_mail_full/tests/test_sms_composer.py22
4 files changed, 110 insertions, 32 deletions
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
index e1dac1fba33..86713fddc24 100644
--- a/addons/sms/models/mail_thread.py
+++ b/addons/sms/models/mail_thread.py
@@ -128,11 +128,8 @@ class MailThread(models.AbstractModel):
'partner_store': True,
'field_store': fname,
}
- elif all_numbers:
- result[record.id] = {'partner': self.env['res.partner'], 'sanitized': False, 'number': all_numbers[0]}
else:
- idx, value = next(((idx, val) for idx, val in enumerate(all_numbers)), (0, False))
- fname = tocheck_fields[idx] if tocheck_fields else False
+ value, fname = next(((value, fname) for value, fname in zip(all_numbers, tocheck_fields) if value), (0, False))
result[record.id] = {
'partner': self.env['res.partner'],
'sanitized': False,
diff --git a/addons/sms/wizard/sms_composer.py b/addons/sms/wizard/sms_composer.py
index 33a9f2a6293..df76d2e9c60 100644
--- a/addons/sms/wizard/sms_composer.py
+++ b/addons/sms/wizard/sms_composer.py
@@ -72,10 +72,16 @@ class SendSMS(models.TransientModel):
mass_force_send = fields.Boolean('Send directly', default=False)
mass_use_blacklist = fields.Boolean('Use blacklist', default=True)
# recipients
- recipient_single_description = fields.Text('Recipients (Partners)', compute='_compute_recipients', compute_sudo=False)
recipient_valid_count = fields.Integer('# Valid recipients', compute='_compute_recipients', compute_sudo=False)
recipient_invalid_count = fields.Integer('# Invalid recipients', compute='_compute_recipients', compute_sudo=False)
- number_field_name = fields.Char(string='Field holding number')
+ recipient_single_description = fields.Text('Recipients (Partners)', compute='_compute_recipient_single', compute_sudo=False)
+ recipient_single_number = fields.Char('Stored Recipient Number', compute='_compute_recipient_single', compute_sudo=False)
+ recipient_single_number_itf = fields.Char(
+ 'Recipient Number', compute='_compute_recipient_single',
+ readonly=False, compute_sudo=False, store=True,
+ help='UX field allowing to edit the recipient number. If changed it will be stored onto the recipient.')
+ recipient_single_valid = fields.Boolean("Is valid", compute='_compute_recipient_single_valid', compute_sudo=False)
+ number_field_name = fields.Char('Number Field')
numbers = fields.Char('Recipients (Numbers)')
sanitized_numbers = fields.Char('Sanitized Number', compute='_compute_sanitized_numbers', compute_sudo=False)
# content
@@ -99,28 +105,50 @@ class SendSMS(models.TransientModel):
@api.depends('res_model', 'res_id', 'res_ids', 'use_active_domain', 'composition_mode', 'number_field_name', 'sanitized_numbers')
def _compute_recipients(self):
for composer in self:
- composer.recipient_single_description = False
composer.recipient_valid_count = 0
composer.recipient_invalid_count = 0
- if composer.composition_mode in ('comment', 'mass') and composer.res_model:
- records = composer._get_records()
+ if composer.composition_mode not in ('comment', 'mass') or not composer.res_model:
+ continue
- if records and issubclass(type(records), self.pool['mail.thread']):
- res = records._sms_get_recipients_info(force_field=composer.number_field_name)
- valid_ids = [rid for rid, rvalues in res.items() if rvalues['sanitized']]
- invalid_ids = [rid for rid, rvalues in res.items() if not rvalues['sanitized']]
- composer.recipient_valid_count = len(valid_ids)
- composer.recipient_invalid_count = len(invalid_ids)
- if len(records) == 1:
- composer.recipient_single_description = '%s (%s)' % (
- res[records.id]['partner'].name or records.display_name,
- res[records.id]['sanitized'] or _("Invalid number")
- )
- else:
- composer.recipient_invalid_count = 0 if (
- composer.sanitized_numbers or (composer.composition_mode == 'mass' and composer.use_active_domain)
- ) else 1
+ records = composer._get_records()
+ if records and issubclass(type(records), self.pool['mail.thread']):
+ res = records._sms_get_recipients_info(force_field=composer.number_field_name, partner_fallback=not composer.comment_single_recipient)
+ composer.recipient_valid_count = len([rid for rid, rvalues in res.items() if rvalues['sanitized']])
+ composer.recipient_invalid_count = len([rid for rid, rvalues in res.items() if not rvalues['sanitized']])
+ else:
+ composer.recipient_invalid_count = 0 if (
+ composer.sanitized_numbers or (composer.composition_mode == 'mass' and composer.use_active_domain)
+ ) else 1
+
+ @api.depends('res_model', 'number_field_name')
+ def _compute_recipient_single(self):
+ for composer in self:
+ records = composer._get_records()
+ if not records or not issubclass(type(records), self.pool['mail.thread']) or not composer.comment_single_recipient:
+ composer.recipient_single_description = False
+ composer.recipient_single_number = ''
+ composer.recipient_single_number_itf = ''
+ continue
+ records.ensure_one()
+ res = records._sms_get_recipients_info(force_field=composer.number_field_name, partner_fallback=False)
+ composer.recipient_single_description = res[records.id]['partner'].name or records.display_name
+ composer.recipient_single_number = res[records.id]['number'] or ''
+ if not composer.recipient_single_number_itf:
+ composer.recipient_single_number_itf = res[records.id]['number'] or ''
+ if not composer.number_field_name:
+ composer.number_field_name = res[records.id]['field_store']
+
+ @api.depends('recipient_single_number', 'recipient_single_number_itf')
+ 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
@api.depends('numbers', 'res_model', 'res_id')
def _compute_sanitized_numbers(self):
@@ -149,8 +177,11 @@ class SendSMS(models.TransientModel):
# ------------------------------------------------------------
def action_send_sms(self):
- if self.composition_mode in ('numbers', 'comment') and self.recipient_invalid_count:
- raise UserError(_('%s invalid recipients') % self.recipient_invalid_count)
+ if self.composition_mode in ('numbers', 'comment'):
+ if self.comment_single_recipient and not self.recipient_single_valid:
+ raise UserError(_('Invalid recipient number. Please update it.'))
+ elif not self.comment_single_recipient and self.recipient_invalid_count:
+ raise UserError(_('%s invalid recipients') % self.recipient_invalid_count)
self._action_send_sms()
return False
@@ -164,9 +195,12 @@ class SendSMS(models.TransientModel):
if self.composition_mode == 'numbers':
return self._action_send_sms_numbers()
elif self.composition_mode == 'comment':
- if records is not None and issubclass(type(records), self.pool['mail.thread']):
+ if records is None or not issubclass(type(records), self.pool['mail.thread']):
+ return self._action_send_sms_numbers()
+ if self.comment_single_recipient:
+ return self._action_send_sms_comment_single(records)
+ else:
return self._action_send_sms_comment(records)
- return self._action_send_sms_numbers()
else:
return self._action_send_sms_mass(records)
@@ -178,6 +212,16 @@ class SendSMS(models.TransientModel):
} for number in self.sanitized_numbers.split(',')])
return True
+ def _action_send_sms_comment_single(self, records=None):
+ # If we have a recipient_single_original number, it's possible this number has been corrected in the popup
+ # if invalid. As a consequence, the test cannot be based on recipient_invalid_count, which count is based
+ # on the numbers in the database.
+ records = records if records is not None else self._get_records()
+ records.ensure_one()
+ if self.recipient_single_number_itf and self.recipient_single_number_itf != self.recipient_single_number:
+ records.write({self.number_field_name: self.recipient_single_number_itf})
+ return self._action_send_sms_comment(records=records)
+
def _action_send_sms_comment(self, records=None):
records = records if records is not None else self._get_records()
subtype_id = self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note')
diff --git a/addons/sms/wizard/sms_composer_views.xml b/addons/sms/wizard/sms_composer_views.xml
index 181b54aa736..543b731f360 100644
--- a/addons/sms/wizard/sms_composer_views.xml
+++ b/addons/sms/wizard/sms_composer_views.xml
@@ -14,10 +14,18 @@
<field name="active_domain" invisible="1"/>
<field name="res_model" invisible="1"/>
<field name="mass_force_send" invisible="1"/>
+ <field name="recipient_single_valid" invisible="1"/>
+ <field name="recipient_single_number" invisible="1"/>
<field name="number_field_name" invisible="1"/>
<field name="numbers" invisible="1"/>
<field name="sanitized_numbers" invisible="1"/>
+ <!-- Single mode information (invalid number) -->
+ <div colspan="2" class="alert alert-danger text-center mb-3" role="alert"
+ attrs="{'invisible': ['|', ('comment_single_recipient', '=', False), ('recipient_single_valid', '=', True)]}">
+ <p class="my-0">Invalid phone number</p>
+ </div>
+
<!-- Mass mode information (res_ids versus active domain) -->
<div colspan="2" class="alert alert-info text-center mb-3" role="alert"
attrs="{'invisible': [('comment_single_recipient', '=', True)]}">
@@ -31,16 +39,25 @@
and <field class="oe_inline font-weight-bold" name="recipient_invalid_count"/> are invalid.
</p>
</div>
- <field name="recipient_single_description" string="Recipients"
- readonly="1"
+
+ <label for="recipient_single_description" string="Recipient"
+ class="font-weight-bold"
attrs="{'invisible': [('comment_single_recipient', '=', False)]}"/>
+ <div attrs="{'invisible': [('comment_single_recipient', '=', False)]}">
+ <field name="recipient_single_description" class="oe_inline"/>
+ <field name="recipient_single_number_itf" class="oe_inline" nolabel="1"/>
+ </div>
+
<field name="body" widget="sms_widget"/>
<field name="mass_keep_log" invisible="1"/>
</group>
</sheet>
<footer>
+ <!-- attrs doesn't work for 'disabled'-->
+ <button string="Send SMS" type="object" class="oe_highlight" name="action_send_sms"
+ attrs="{'invisible': ['|',('composition_mode', 'not in', ('comment', 'numbers')),('recipient_single_valid', '=', False)]}"/>
<button string="Send SMS" type="object" class="oe_highlight" name="action_send_sms"
- attrs="{'invisible': [('composition_mode', 'not in', ('comment', 'numbers'))]}"/>
+ attrs="{'invisible': ['|',('composition_mode', 'not in', ('comment', 'numbers')),('recipient_single_valid', '=', True)]}" disabled='1'/>
<button string="Put in queue" type="object" class="oe_highlight" name="action_send_sms"
attrs="{'invisible': [('composition_mode', '!=', 'mass')]}"/>
<button string="Send Now" type="object" name="action_send_sms_mass_now"
diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py
index 0dbe6636b99..4fc596c1985 100644
--- a/addons/test_mail_full/tests/test_sms_composer.py
+++ b/addons/test_mail_full/tests/test_sms_composer.py
@@ -131,10 +131,30 @@ class TestSMSComposerComment(test_mail_full_common.TestSMSCommon, test_mail_full
self.assertEqual(composer.res_id, self.test_record.id)
self.assertEqual(composer.number_field_name, 'phone_nbr')
self.assertTrue(composer.comment_single_recipient)
- self.assertEqual(composer.recipient_single_description, '%s (%s)' % (self.test_record.customer_id.display_name, self.test_numbers_san[1]))
+ self.assertEqual(composer.recipient_single_description, self.test_record.customer_id.display_name)
+ self.assertEqual(composer.recipient_single_number, self.test_numbers[1])
+ self.assertEqual(composer.recipient_single_number_itf, self.test_numbers[1])
+ self.assertTrue(composer.recipient_single_valid)
self.assertEqual(composer.recipient_valid_count, 1)
self.assertEqual(composer.recipient_invalid_count, 0)
+ with self.with_user('employee'):
+ composer.update({'recipient_single_number_itf': '0123456789'})
+
+ self.assertFalse(composer.recipient_single_valid)
+
+ with self.with_user('employee'):
+ composer.update({'recipient_single_number_itf': self.random_numbers[0]})
+
+ self.assertTrue(composer.recipient_single_valid)
+
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ self.test_record.flush()
+ self.assertEqual(self.test_record.phone_nbr, self.random_numbers[0])
+
def test_composer_numbers_no_model(self):
with self.with_user('employee'):
composer = self.env['sms.composer'].with_context(