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/test_mail_full | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/test_mail_full')
20 files changed, 2902 insertions, 0 deletions
diff --git a/addons/test_mail_full/__init__.py b/addons/test_mail_full/__init__.py new file mode 100644 index 00000000..567548c7 --- /dev/null +++ b/addons/test_mail_full/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import tests diff --git a/addons/test_mail_full/__manifest__.py b/addons/test_mail_full/__manifest__.py new file mode 100644 index 00000000..586ee908 --- /dev/null +++ b/addons/test_mail_full/__manifest__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Mail Tests (Full)', + 'version': '1.0', + 'category': 'Hidden', + 'sequence': 9876, + 'summary': 'Mail Tests: performances and tests specific to mail with all sub-modules', + 'description': """This module contains tests related to various mail features +and mail-related sub modules. Those tests are present in a separate module as it +contains models used only to perform tests independently to functional aspects of +real applications. """, + 'depends': [ + 'test_mail', + 'test_mass_mailing', + 'mail', + 'mail_bot', + # 'snailmail', + 'mass_mailing', + 'mass_mailing_sms', + 'phone_validation', + 'sms', + ], + 'data': [ + 'security/ir.model.access.csv', + ], + 'demo': [ + ], + 'installable': True, + 'application': False, + 'license': 'LGPL-3', +} diff --git a/addons/test_mail_full/i18n/test_mail_full.pot b/addons/test_mail_full/i18n/test_mail_full.pot new file mode 100644 index 00000000..75ac8eee --- /dev/null +++ b/addons/test_mail_full/i18n/test_mail_full.pot @@ -0,0 +1,291 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * test_mail_full +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server saas~12.5\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-09 10:49+0000\n" +"PO-Revision-Date: 2019-09-09 10:49+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_needaction +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_needaction +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_attachment_count +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_attachment_count +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: test_mail_full +#: model:ir.model,name:test_mail_full.model_mail_test_sms +#: model:ir.model,name:test_mail_full.model_mail_test_sms_bl +msgid "Chatter Model for SMS Gateway" +msgstr "" + +#. module: test_mail_full +#: model:ir.model,name:test_mail_full.model_mail_test_sms_partner +msgid "Chatter Model for SMS Gateway (Partner only)" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__create_uid +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__create_uid +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__create_uid +msgid "Created by" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__create_date +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__create_date +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__create_date +msgid "Created on" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__customer_id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__customer_id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__partner_id +msgid "Customer" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__display_name +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__display_name +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__display_name +msgid "Display Name" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__email_from +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__email_from +msgid "Email From" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__phone_sanitized +msgid "" +"Field used to store sanitized phone number. Helps speeding up searches and " +"comparisons." +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_follower_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_follower_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_channel_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_channel_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_partner_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_partner_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__id +msgid "ID" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_needaction +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_unread +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_needaction +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_unread +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_needaction +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_error +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_sms_error +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_error +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_sms_error +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_error +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__phone_blacklisted +msgid "" +"If the email address is on the blacklist, the contact won't receive mass " +"mailing anymore, from any list" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_is_follower +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_is_follower +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms____last_update +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl____last_update +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner____last_update +msgid "Last Modified on" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__write_uid +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__write_uid +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__write_date +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__write_date +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__write_date +msgid "Last Updated on" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_main_attachment_id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_main_attachment_id +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_error +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_error +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_ids +msgid "Messages" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__mobile_nbr +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__mobile_nbr +msgid "Mobile Nbr" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__name +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__name +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__name +msgid "Name" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_needaction_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_needaction_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_error_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_error_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_needaction_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_needaction_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_error_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_error_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_unread_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_unread_counter +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_blacklisted +msgid "Phone Blacklisted" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__phone_nbr +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_nbr +msgid "Phone Nbr" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_sms_error +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_sms_error +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_sanitized +msgid "Sanitized Number" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__subject +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__subject +msgid "Subject" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_unread +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_unread +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_unread_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_unread_counter +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__website_message_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__website_message_ids +#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: test_mail_full +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__website_message_ids +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__website_message_ids +#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__website_message_ids +msgid "Website communication history" +msgstr "" diff --git a/addons/test_mail_full/models/__init__.py b/addons/test_mail_full/models/__init__.py new file mode 100644 index 00000000..0fe839f2 --- /dev/null +++ b/addons/test_mail_full/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import test_mail_models +from . import mailing_mailing diff --git a/addons/test_mail_full/models/mailing_mailing.py b/addons/test_mail_full/models/mailing_mailing.py new file mode 100644 index 00000000..8d8a49a1 --- /dev/null +++ b/addons/test_mail_full/models/mailing_mailing.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging + +from odoo import models + +_logger = logging.getLogger(__name__) + + +class Mailing(models.Model): + _inherit = 'mailing.mailing' + + def _get_opt_out_list_sms(self): + """Returns a set of emails opted-out in target model""" + self.ensure_one() + if self.mailing_model_real in ('mail.test.sms.bl.optout', + 'mail.test.sms.partner', + 'mail.test.sms.partner.2many'): + res_ids = self._get_recipients() + opt_out_contacts = set(self.env[self.mailing_model_real].search([ + ('id', 'in', res_ids), + ('opt_out', '=', True) + ]).ids) + _logger.info( + "Mass-mailing %s targets %s, optout: %s emails", + self, self.mailing_model_real, len(opt_out_contacts)) + return opt_out_contacts + return super(Mailing, self)._get_opt_out_list_sms() diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py new file mode 100644 index 00000000..a3b5eb8d --- /dev/null +++ b/addons/test_mail_full/models/test_mail_models.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class MailTestSMS(models.Model): + """ A model inheriting from mail.thread with some fields used for SMS + gateway, like a partner, a specific mobile phone, ... """ + _description = 'Chatter Model for SMS Gateway' + _name = 'mail.test.sms' + _inherit = ['mail.thread'] + _order = 'name asc, id asc' + + name = fields.Char() + subject = fields.Char() + email_from = fields.Char() + phone_nbr = fields.Char() + mobile_nbr = fields.Char() + customer_id = fields.Many2one('res.partner', 'Customer') + + def _sms_get_partner_fields(self): + return ['customer_id'] + + def _sms_get_number_fields(self): + return ['phone_nbr', 'mobile_nbr'] + + +class MailTestSMSBL(models.Model): + """ A model inheriting from mail.thread.phone allowing to test auto formatting + of phone numbers, blacklist, ... """ + _description = 'SMS Mailing Blacklist Enabled' + _name = 'mail.test.sms.bl' + _inherit = ['mail.thread.phone'] + _order = 'name asc, id asc' + + name = fields.Char() + subject = fields.Char() + email_from = fields.Char() + phone_nbr = fields.Char() + mobile_nbr = fields.Char() + customer_id = fields.Many2one('res.partner', 'Customer') + + def _sms_get_partner_fields(self): + return ['customer_id'] + + def _sms_get_number_fields(self): + # TDE note: should override _phone_get_number_fields but ok as sms in dependencies + return ['phone_nbr', 'mobile_nbr'] + + +class MailTestSMSOptout(models.Model): + """ Model using blacklist mechanism and a hijacked opt-out mechanism for + mass mailing features. """ + _description = 'SMS Mailing Blacklist / Optout Enabled' + _name = 'mail.test.sms.bl.optout' + _inherit = ['mail.thread.phone'] + _order = 'name asc, id asc' + + name = fields.Char() + subject = fields.Char() + email_from = fields.Char() + phone_nbr = fields.Char() + mobile_nbr = fields.Char() + customer_id = fields.Many2one('res.partner', 'Customer') + opt_out = fields.Boolean() + + def _sms_get_partner_fields(self): + return ['customer_id'] + + def _sms_get_number_fields(self): + # TDE note: should override _phone_get_number_fields but ok as sms in dependencies + return ['phone_nbr', 'mobile_nbr'] + + +class MailTestSMSPartner(models.Model): + """ A model like sale order having only a customer, not specific phone + or mobile fields. """ + _description = 'Chatter Model for SMS Gateway (Partner only)' + _name = 'mail.test.sms.partner' + _inherit = ['mail.thread'] + + name = fields.Char() + customer_id = fields.Many2one('res.partner', 'Customer') + opt_out = fields.Boolean() + + def _sms_get_partner_fields(self): + return ['customer_id'] + + def _sms_get_number_fields(self): + return [] + + +class MailTestSMSPartner2Many(models.Model): + """ A model like sale order having only a customer, not specific phone + or mobile fields. """ + _description = 'Chatter Model for SMS Gateway (M2M Partners only)' + _name = 'mail.test.sms.partner.2many' + _inherit = ['mail.thread'] + + name = fields.Char() + customer_ids = fields.Many2many('res.partner', string='Customers') + opt_out = fields.Boolean() + + def _sms_get_partner_fields(self): + return ['customer_ids'] + + def _sms_get_number_fields(self): + return [] diff --git a/addons/test_mail_full/security/ir.model.access.csv b/addons/test_mail_full/security/ir.model.access.csv new file mode 100644 index 00000000..5a099a7d --- /dev/null +++ b/addons/test_mail_full/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mail_test_sms_all,mail.test.sms.all,model_mail_test_sms,,0,0,0,0 +access_mail_test_sms_user,mail.test.sms.user,model_mail_test_sms,base.group_user,1,1,1,1 +access_mail_test_sms_bl_all,mail.test.sms.bl.all,model_mail_test_sms_bl,,0,0,0,0 +access_mail_test_sms_bl_user,mail.test.sms.bl.user,model_mail_test_sms_bl,base.group_user,1,1,1,1 +access_mail_test_sms_bl_optout_all,mail.test.sms.bl.optout.all,model_mail_test_sms_bl_optout,,0,0,0,0 +access_mail_test_sms_bl_optout_user,mail.test.sms.bl.optout.user,model_mail_test_sms_bl_optout,base.group_user,1,1,1,1 +access_mail_test_sms_partner_all,mail.test.sms.partner.all,model_mail_test_sms_partner,,0,0,0,0 +access_mail_test_sms_partner_user,mail.test.sms.partner.user,model_mail_test_sms_partner,base.group_user,1,1,1,1 +access_mail_test_sms_partner_2many_all,mail.test.sms.partner.2many.all,model_mail_test_sms_partner_2many,,0,0,0,0 +access_mail_test_sms_partner_2many_user,mail.test.sms.partner.2many.user,model_mail_test_sms_partner_2many,base.group_user,1,1,1,1 diff --git a/addons/test_mail_full/tests/__init__.py b/addons/test_mail_full/tests/__init__.py new file mode 100644 index 00000000..9c4bf096 --- /dev/null +++ b/addons/test_mail_full/tests/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from . import common +from . import test_odoobot +from . import test_phone_blacklist +from . import test_mass_mailing +from . import test_mass_sms +from . import test_sms_composer +from . import test_sms_management +from . import test_sms_performance +from . import test_sms_post +from . import test_sms_server_actions +from . import test_sms_sms +from . import test_sms_template diff --git a/addons/test_mail_full/tests/common.py b/addons/test_mail_full/tests/common.py new file mode 100644 index 00000000..e41d4880 --- /dev/null +++ b/addons/test_mail_full/tests/common.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.phone_validation.tools import phone_validation +from odoo.addons.mass_mailing_sms.tests.common import MassSMSCommon +from odoo.addons.test_mail.tests.common import TestRecipients +from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon + + +class TestMailFullCommon(TestMassMailCommon, MassSMSCommon): + + @classmethod + def setUpClass(cls): + super(TestMailFullCommon, cls).setUpClass() + + cls.mailing_sms = cls.env['mailing.mailing'].with_user(cls.user_marketing).create({ + 'name': 'XMas SMS', + 'subject': 'Xmas SMS for {object.name}', + 'mailing_model_id': cls.env['ir.model']._get('mail.test.sms').id, + 'mailing_type': 'sms', + 'mailing_domain': '%s' % repr([('name', 'ilike', 'SMSTest')]), + 'body_plaintext': 'Dear ${object.display_name} this is a mass SMS with two links http://www.odoo.com/smstest and http://www.odoo.com/smstest/${object.id}', + 'sms_force_send': True, + 'sms_allow_unsubscribe': True, + }) + + @classmethod + def _create_mailing_sms_test_records(cls, model='mail.test.sms', partners=None, count=1): + """ Helper to create data. Currently simple, to be improved. """ + Model = cls.env[model] + phone_field = 'phone_nbr' if 'phone_nbr' in Model else 'phone' + partner_field = 'customer_id' if 'customer_id' in Model else 'partner_id' + + vals_list = [] + for idx in range(count): + vals = { + 'name': 'SMSTestRecord_%02d' % idx, + phone_field: '045600%02d%02d' % (idx, idx) + } + if partners: + vals[partner_field] = partners[idx % len(partners)] + + vals_list.append(vals) + + return cls.env[model].create(vals_list) + + +class TestRecipients(TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestRecipients, cls).setUpClass() + cls.partner_numbers = [ + phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164') + for partner in (cls.partner_1 | cls.partner_2) + ] diff --git a/addons/test_mail_full/tests/test_mass_mailing.py b/addons/test_mail_full/tests/test_mass_mailing.py new file mode 100644 index 00000000..a01804df --- /dev/null +++ b/addons/test_mail_full/tests/test_mass_mailing.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import werkzeug + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon +from odoo.tests.common import users +from odoo.tools import mute_logger +from odoo.tests import tagged + + +@tagged('mass_mailing') +class TestMassMailing(TestMailFullCommon): + + @classmethod + def setUpClass(cls): + super(TestMassMailing, cls).setUpClass() + + @users('user_marketing') + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_mailing_w_blacklist_opt_out(self): + mailing = self.env['mailing.mailing'].browse(self.mailing_bl.ids) + + mailing.write({'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id}) + recipients = self._create_mailing_test_records(model='mailing.test.optout', count=10) + + # optout records 1 and 2 + (recipients[1] | recipients[2]).write({'opt_out': True}) + # blacklist records 3 and 4 + self.env['mail.blacklist'].create({'email': recipients[3].email_normalized}) + self.env['mail.blacklist'].create({'email': recipients[4].email_normalized}) + # have a duplicate email for 9 + recipient_dup_1 = recipients[9].copy() + # have a void mail + recipient_void_1 = self.env['mailing.test.optout'].create({'name': 'TestRecord_void_1'}) + # have a falsy mail + recipient_falsy_1 = self.env['mailing.test.optout'].create({ + 'name': 'TestRecord_falsy_1', + 'email_from': 'falsymail' + }) + recipients_all = recipients + recipient_dup_1 + recipient_void_1 + recipient_falsy_1 + + mailing.write({'mailing_domain': [('id', 'in', recipients_all.ids)]}) + mailing.action_put_in_queue() + with self.mock_mail_gateway(mail_unlink_sent=False): + mailing.action_send_mail() + + for recipient in recipients_all: + recipient_info = { + 'email': recipient.email_normalized, + 'content': 'Hello %s' % recipient.name} + # opt-out: ignored (cancel mail) + if recipient in recipients[1] | recipients[2]: + recipient_info['state'] = 'ignored' + # blacklisted: ignored (cancel mail) + elif recipient in recipients[3] | recipients[4]: + recipient_info['state'] = 'ignored' + # duplicates: ignored (cancel mail) + elif recipient == recipient_dup_1: + recipient_info['state'] = 'ignored' + # void: ignored (cancel mail) + elif recipient == recipient_void_1: + recipient_info['state'] = 'ignored' + # falsy: ignored (cancel mail) + elif recipient == recipient_falsy_1: + recipient_info['state'] = 'ignored' + recipient_info['email'] = recipient.email_from # normalized is False but email should be falsymail + else: + email = self._find_sent_mail_wemail(recipient.email_normalized) + # preview correctly integrated rendered jinja + self.assertIn( + 'Hi %s :)' % recipient.name, + email['body']) + # rendered unsubscribe + self.assertIn( + '%s/mail/mailing/%s/unsubscribe' % (mailing.get_base_url(), mailing.id), + email['body']) + unsubscribe_href = self._get_href_from_anchor_id(email['body'], "url6") + unsubscribe_url = werkzeug.urls.url_parse(unsubscribe_href) + unsubscribe_params = unsubscribe_url.decode_query().to_dict(flat=True) + self.assertEqual(int(unsubscribe_params['res_id']), recipient.id) + self.assertEqual(unsubscribe_params['email'], recipient.email_normalized) + self.assertEqual( + mailing._unsubscribe_token(unsubscribe_params['res_id'], (unsubscribe_params['email'])), + unsubscribe_params['token'] + ) + # rendered view + self.assertIn( + '%s/mailing/%s/view' % (mailing.get_base_url(), mailing.id), + email['body']) + view_href = self._get_href_from_anchor_id(email['body'], "url6") + view_url = werkzeug.urls.url_parse(view_href) + view_params = view_url.decode_query().to_dict(flat=True) + self.assertEqual(int(view_params['res_id']), recipient.id) + self.assertEqual(view_params['email'], recipient.email_normalized) + self.assertEqual( + mailing._unsubscribe_token(view_params['res_id'], (view_params['email'])), + view_params['token'] + ) + + self.assertMailTraces( + [recipient_info], mailing, recipient, + mail_links_info=[[ + ('url0', 'https://www.odoo.tz/my/%s' % recipient.name, True, {}), + ('url1', 'https://www.odoo.be', True, {}), + ('url2', 'https://www.odoo.com', True, {}), + ('url3', 'https://www.odoo.eu', True, {}), + ('url4', 'https://www.example.com/foo/bar?baz=qux', True, {'baz': 'qux'}), + ('url5', '%s/event/dummy-event-0' % mailing.get_base_url(), True, {}), + # view is not shortened and parsed at sending + ('url6', '%s/view' % mailing.get_base_url(), False, {}), + ('url7', 'mailto:test@odoo.com', False, {}), + # unsubscribe is not shortened and parsed at sending + ('url8', '%s/unsubscribe_from_list' % mailing.get_base_url(), False, {}), + ]], + check_mail=True,) + + # sent: 13, 2 bl, 2 opt-out, 3 invalid -> 6 remaining + self.assertMailingStatistics(mailing, expected=13, delivered=6, sent=6, ignored=7) diff --git a/addons/test_mail_full/tests/test_mass_sms.py b/addons/test_mail_full/tests/test_mass_sms.py new file mode 100644 index 00000000..dbc91b95 --- /dev/null +++ b/addons/test_mail_full/tests/test_mass_sms.py @@ -0,0 +1,405 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from ast import literal_eval + +from odoo.addons.phone_validation.tools import phone_validation +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon +from odoo import exceptions +from odoo.tests import tagged +from odoo.tests.common import users +from odoo.tools import mute_logger + + +@tagged('mass_mailing') +class TestMassSMSCommon(TestMailFullCommon): + + @classmethod + def setUpClass(cls): + super(TestMassSMSCommon, cls).setUpClass() + cls._test_body = 'Mass SMS in your face' + + records = cls.env['mail.test.sms'] + partners = cls.env['res.partner'] + country_be_id = cls.env.ref('base.be').id, + country_us_id = cls.env.ref('base.us').id, + + for x in range(10): + partners += cls.env['res.partner'].with_context(**cls._test_context).create({ + 'name': 'Partner_%s' % (x), + 'email': '_test_partner_%s@example.com' % (x), + 'country_id': country_be_id, + 'mobile': '045600%s%s99' % (x, x) + }) + records += cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'MassSMSTest_%s' % (x), + 'customer_id': partners[x].id, + 'phone_nbr': '045600%s%s44' % (x, x) + }) + cls.records = cls._reset_mail_context(records) + cls.records_numbers = [phone_validation.phone_format(r.phone_nbr, 'BE', '32', force_format='E164') for r in cls.records] + cls.partners = partners + + cls.sms_template = cls.env['sms.template'].create({ + 'name': 'Test Template', + 'model_id': cls.env['ir.model']._get('mail.test.sms').id, + 'body': 'Dear ${object.display_name} this is a mass SMS.', + }) + + cls.partner_numbers = [ + phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164') + for partner in partners + ] + + cls.mailing = cls.env['mailing.mailing'].with_user(cls.user_marketing).create({ + 'name': 'Xmas Spam', + 'subject': 'Xmas Spam', + 'mailing_model_id': cls.env['ir.model']._get('mail.test.sms').id, + 'mailing_type': 'sms', + 'mailing_domain': '%s' % repr([('name', 'ilike', 'MassSMSTest')]), + 'sms_template_id': cls.sms_template.id, + 'sms_allow_unsubscribe': False, + }) + + +@tagged('mass_mailing') +class TestMassSMSInternals(TestMassSMSCommon): + + @users('user_marketing') + def test_mass_sms_domain(self): + mailing = self.env['mailing.mailing'].create({ + 'name': 'Xmas Spam', + 'subject': 'Xmas Spam', + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id, + 'mailing_type': 'sms', + }) + self.assertEqual(literal_eval(mailing.mailing_domain), []) + + mailing = self.env['mailing.mailing'].create({ + 'name': 'Xmas Spam', + 'subject': 'Xmas Spam', + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.bl').id, + 'mailing_type': 'sms', + }) + self.assertEqual(literal_eval(mailing.mailing_domain), [('phone_sanitized_blacklisted', '=', False)]) + + @users('user_marketing') + def test_mass_sms_internals(self): + with self.with_user('user_marketing'): + mailing = self.env['mailing.mailing'].create({ + 'name': 'Xmas Spam', + 'subject': 'Xmas Spam', + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id, + 'mailing_type': 'sms', + 'mailing_domain': '%s' % repr([('name', 'ilike', 'MassSMSTest')]), + 'sms_template_id': self.sms_template.id, + 'sms_allow_unsubscribe': False, + }) + + self.assertEqual(mailing.mailing_model_real, 'mail.test.sms') + self.assertEqual(mailing.medium_id, self.env.ref('mass_mailing_sms.utm_medium_sms')) + self.assertEqual(mailing.body_plaintext, self.sms_template.body) + + remaining_res_ids = mailing._get_remaining_recipients() + self.assertEqual(set(remaining_res_ids), set(self.records.ids)) + + with self.mockSMSGateway(): + mailing.action_send_sms() + + self.assertSMSTraces( + [{'partner': record.customer_id, + 'number': self.records_numbers[i], + 'content': 'Dear %s this is a mass SMS.' % record.display_name + } for i, record in enumerate(self.records)], + mailing, self.records, + ) + + def test_mass_sms_internals_errors(self): + # same customer, specific different number on record -> should be valid + new_record_1 = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_nr1', + 'customer_id': self.partners[0].id, + 'phone_nbr': '0456999999', + }) + void_record = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_void', + 'customer_id': False, + 'phone_nbr': '', + }) + falsy_record_1 = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_falsy_1', + 'customer_id': False, + 'phone_nbr': 'abcd', + }) + falsy_record_2 = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_falsy_2', + 'customer_id': False, + 'phone_nbr': '04561122', + }) + bl_record_1 = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_bl_1', + 'customer_id': False, + 'phone_nbr': '0456110011', + }) + self.env['phone.blacklist'].create({'number': '0456110011'}) + # new customer, number already on record -> should be ignored + country_be_id = self.env.ref('base.be').id + nr2_partner = self.env['res.partner'].create({ + 'name': 'Partner_nr2', + 'country_id': country_be_id, + 'mobile': '0456449999', + }) + new_record_2 = self.env['mail.test.sms'].create({ + 'name': 'MassSMSTest_nr2', + 'customer_id': nr2_partner.id, + 'phone_nbr': self.records[0].phone_nbr, + }) + records_numbers = self.records_numbers + ['+32456999999'] + + with self.with_user('user_marketing'): + with self.mockSMSGateway(): + self.mailing.action_send_sms() + + self.assertSMSTraces( + [{'partner': record.customer_id, 'number': records_numbers[i], + 'content': 'Dear %s this is a mass SMS.' % record.display_name} + for i, record in enumerate(self.records | new_record_1)], + self.mailing, self.records | new_record_1, + ) + # duplicates + self.assertSMSTraces( + [{'partner': new_record_2.customer_id, 'number': self.records_numbers[0], + 'content': 'Dear %s this is a mass SMS.' % new_record_2.display_name, 'state': 'ignored', + 'failure_type': 'sms_duplicate'}], + self.mailing, new_record_2, + ) + # blacklist + self.assertSMSTraces( + [{'partner': self.env['res.partner'], 'number': phone_validation.phone_format(bl_record_1.phone_nbr, 'BE', '32', force_format='E164'), + 'content': 'Dear %s this is a mass SMS.' % bl_record_1.display_name, 'state': 'ignored', + 'failure_type': 'sms_blacklist'}], + self.mailing, bl_record_1, + ) + # missing number + self.assertSMSTraces( + [{'partner': self.env['res.partner'], 'number': False, + 'content': 'Dear %s this is a mass SMS.' % void_record.display_name, 'state': 'exception', + 'failure_type': 'sms_number_missing'}], + self.mailing, void_record, + ) + # wrong values + self.assertSMSTraces( + [{'partner': self.env['res.partner'], 'number': record.phone_nbr, + 'content': 'Dear %s this is a mass SMS.' % record.display_name, 'state': 'bounced', + 'failure_type': 'sms_number_format'} + for record in falsy_record_1 + falsy_record_2], + self.mailing, falsy_record_1 + falsy_record_2, + ) + + def test_mass_sms_internals_done_ids(self): + with self.with_user('user_marketing'): + with self.mockSMSGateway(): + self.mailing.action_send_sms(res_ids=self.records[:5].ids) + + traces = self.env['mailing.trace'].search([('mass_mailing_id', 'in', self.mailing.ids)]) + self.assertEqual(len(traces), 5) + # new traces generated + self.assertSMSTraces( + [{'partner': record.customer_id, 'number': self.records_numbers[i], + 'content': 'Dear %s this is a mass SMS.' % record.display_name} + for i, record in enumerate(self.records[:5])], + self.mailing, self.records[:5], + ) + + with self.with_user('user_marketing'): + with self.mockSMSGateway(): + self.mailing.action_send_sms(res_ids=self.records.ids) + + # delete old traces (for testing purpose: ease check by deleting old ones) + traces.unlink() + # new failed traces generated for duplicates + self.assertSMSTraces( + [{'partner': record.customer_id, 'number': self.records_numbers[i], + 'content': 'Dear %s this is a mass SMS.' % record.display_name, 'state': 'ignored', + 'failure_type': 'sms_duplicate'} + for i, record in enumerate(self.records[:5])], + self.mailing, self.records[:5], + ) + # new traces generated + self.assertSMSTraces( + [{'partner': record.customer_id, 'number': self.records_numbers[i+5], + 'content': 'Dear %s this is a mass SMS.' % record.display_name} + for i, record in enumerate(self.records[5:])], + self.mailing, self.records[5:], + ) + + @mute_logger('odoo.addons.mail.models.mail_render_mixin') + def test_mass_sms_test_button(self): + mailing = self.env['mailing.mailing'].create({ + 'name': 'TestButton', + 'subject': 'Subject ${object.name}', + 'preview': 'Preview ${object.name}', + 'state': 'draft', + 'mailing_type': 'sms', + 'body_plaintext': 'Hello ${object.name}', + 'mailing_model_id': self.env['ir.model']._get('res.partner').id, + }) + mailing_test = self.env['mailing.sms.test'].with_user(self.user_marketing).create({ + 'numbers': '+32456001122', + 'mailing_id': mailing.id, + }) + + with self.with_user('user_marketing'): + with self.mockSMSGateway(): + mailing_test.action_send_sms() + + # Test if bad jinja in the body raises an error + mailing.write({ + 'body_plaintext': 'Hello ${object.name_id.id}', + }) + + with self.with_user('user_marketing'): + with self.mock_mail_gateway(), self.assertRaises(Exception): + mailing_test.action_send_sms() + + +@tagged('mass_mailing', 'mass_mailing_sms') +class TestMassSMS(TestMassSMSCommon): + + @users('user_marketing') + def test_mass_sms_links(self): + mailing = self.env['mailing.mailing'].browse(self.mailing.ids) + mailing.write({ + 'body_plaintext': 'Dear ${object.display_name} this is a mass SMS with two links http://www.odoo.com/smstest and http://www.odoo.com/smstest/${object.name}', + 'sms_template_id': False, + 'sms_force_send': True, + 'sms_allow_unsubscribe': True, + }) + + with self.mockSMSGateway(): + mailing.action_send_sms() + + self.assertSMSTraces( + [{'partner': record.customer_id, + 'number': self.records_numbers[i], + 'state': 'sent', + 'content': 'Dear %s this is a mass SMS with two links' % record.display_name + } for i, record in enumerate(self.records)], + mailing, self.records, + sms_links_info=[[ + ('http://www.odoo.com/smstest', True, {}), + ('http://www.odoo.com/smstest/%s' % record.name, True, {}), + # unsubscribe is not shortened and parsed at sending + ('unsubscribe', False, {}), + ] for record in self.records], + ) + + @users('user_marketing') + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_mass_sms_partner_only(self): + """ Check sending SMS marketing on models having only a partner_id fields + set is working. """ + mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids) + mailing.write({ + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner').id, + }) + + records = self.env['mail.test.sms.partner'].create([ + {'name': 'SMSTest on %s' % partner.name, + 'customer_id': partner.id, + } for partner in self.partners + ]) + + with self.mockSMSGateway(): + mailing.action_send_sms() + + self.assertEqual(len(mailing.mailing_trace_ids), 10) + self.assertSMSTraces( + [{'partner': record.customer_id, + 'number': record.customer_id.phone_sanitized, + 'state': 'sent', + 'content': 'Dear %s this is a mass SMS with two links' % record.display_name + } for record in records], + mailing, records, + sms_links_info=[[ + ('http://www.odoo.com/smstest', True, {}), + ('http://www.odoo.com/smstest/%s' % record.id, True, {}), + # unsubscribe is not shortened and parsed at sending + ('unsubscribe', False, {}), + ] for record in records], + ) + + # add a new record, send -> sent list should not resend traces + new_record = self.env['mail.test.sms.partner'].create([ + {'name': 'Duplicate SMS on %s' % self.partners[0].name, + 'customer_id': self.partners[0].id, + } + ]) + with self.mockSMSGateway(): + mailing.action_send_sms() + + self.assertEqual(len(mailing.mailing_trace_ids), 11) + self.assertSMSTraces( + [{'partner': new_record.customer_id, + 'number': new_record.customer_id.phone_sanitized, + 'state': 'sent', + 'content': 'Dear %s this is a mass SMS with two links' % new_record.display_name + }], + mailing, new_record, + sms_links_info=[[ + ('http://www.odoo.com/smstest', True, {}), + ('http://www.odoo.com/smstest/%s' % new_record.id, True, {}), + # unsubscribe is not shortened and parsed at sending + ('unsubscribe', False, {}), + ]], + ) + + @users('user_marketing') + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_mass_sms_partner_only_m2m(self): + """ Check sending SMS marketing on models having only a m2m to partners + is currently not suppored. """ + mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids) + mailing.write({ + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner.2many').id, + }) + + records = self.env['mail.test.sms.partner.2many'].create([ + {'name': 'SMSTest on %s' % partner.name, + 'customer_ids': [(4, partner.id)], + } for partner in self.partners + ]) + + with self.assertRaises(exceptions.UserError), self.mockSMSGateway(): + mailing.action_send_sms() + + + @users('user_marketing') + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_mass_sms_w_opt_out(self): + mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids) + recipients = self._create_mailing_sms_test_records(model='mail.test.sms.bl.optout', count=5) + + # optout records 0 and 1 + (recipients[0] | recipients[1]).write({'opt_out': True}) + # blacklist records 4 + # TDE FIXME: sudo should not be necessary + self.env['phone.blacklist'].sudo().create({'number': recipients[4].phone_nbr}) + + mailing.write({ + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.bl.optout'), + 'mailing_domain': [('id', 'in', recipients.ids)], + }) + + with self.mockSMSGateway(): + mailing.action_send_sms() + + self.assertSMSTraces( + [{'number': '+32456000000', 'state': 'ignored', 'failure_type': 'sms_blacklist'}, # TDE FIXME: should be opt_out + {'number': '+32456000101', 'state': 'ignored', 'failure_type': 'sms_blacklist'}, # TDE FIXME: should be opt_out + {'number': '+32456000202', 'state': 'sent'}, + {'number': '+32456000303', 'state': 'sent'}, + {'number': '+32456000404', 'state': 'ignored', 'failure_type': 'sms_blacklist'}], + mailing, recipients + ) + self.assertEqual(mailing.ignored, 3) diff --git a/addons/test_mail_full/tests/test_odoobot.py b/addons/test_mail_full/tests/test_odoobot.py new file mode 100644 index 00000000..ddd6015b --- /dev/null +++ b/addons/test_mail_full/tests/test_odoobot.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from unittest.mock import patch + +from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients +from odoo.tests import tagged +from odoo.tools import mute_logger + + +@tagged("odoobot") +class TestOdoobot(TestMailCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestOdoobot, cls).setUpClass() + cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'}) + + cls.odoobot = cls.env.ref("base.partner_root") + cls.message_post_default_kwargs = { + 'body': '', + 'attachment_ids': [], + 'message_type': 'comment', + 'partner_ids': [], + 'subtype_xmlid': 'mail.mt_comment' + } + cls.odoobot_ping_body = '<a href="http://odoo.com/web#model=res.partner&id=%s" class="o_mail_redirect" data-oe-id="%s" data-oe-model="res.partner" target="_blank">@OdooBot</a>' % (cls.odoobot.id, cls.odoobot.id) + cls.test_record_employe = cls.test_record.with_user(cls.user_employee) + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_fetch_listener(self): + channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot() + partners = self.env['mail.channel'].channel_fetch_listeners(channel.uuid) + odoobot = self.env.ref("base.partner_root") + odoobot_in_fetch_listeners = [partner for partner in partners if partner['id'] == odoobot.id] + self.assertEqual(len(odoobot_in_fetch_listeners), 1, 'odoobot should appear only once in channel_fetch_listeners') + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_odoobot_ping(self): + kwargs = self.message_post_default_kwargs.copy() + kwargs.update({'body': self.odoobot_ping_body, 'partner_ids': [self.odoobot.id, self.user_admin.partner_id.id]}) + + with patch('random.choice', lambda x: x[0]): + self.assertNextMessage( + self.test_record_employe.with_context({'mail_post_autofollow': True}).message_post(**kwargs), + sender=self.odoobot, + answer=False + ) + # Odoobot should not be a follower but user_employee and user_admin should + follower = self.test_record.message_follower_ids.mapped('partner_id') + self.assertNotIn(self.odoobot, follower) + self.assertIn(self.user_employee.partner_id, follower) + self.assertIn(self.user_admin.partner_id, follower) + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_onboarding_flow(self): + kwargs = self.message_post_default_kwargs.copy() + channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot() + + kwargs['body'] = 'tagada 😊' + last_message = self.assertNextMessage( + channel.message_post(**kwargs), + sender=self.odoobot, + answer=("help",) + ) + channel.execute_command(command="help") + self.assertNextMessage( + last_message, # no message will be post with command help, use last odoobot message instead + sender=self.odoobot, + answer=("@OdooBot",) + ) + kwargs['body'] = '' + kwargs['partner_ids'] = [self.env['ir.model.data'].xmlid_to_res_id("base.partner_root")] + self.assertNextMessage( + channel.message_post(**kwargs), + sender=self.odoobot, + answer=("attachment",) + ) + kwargs['body'] = '' + attachment = self.env['ir.attachment'].with_user(self.user_employee).create({ + 'datas': 'bWlncmF0aW9uIHRlc3Q=', + 'name': 'picture_of_your_dog.doc', + 'res_model': 'mail.compose.message', + }) + kwargs['attachment_ids'] = [attachment.id] + # For the end of the flow, we only test that the state changed, but not to which + # one since it depends on the intalled apps, which can add more steps (like livechat) + channel.message_post(**kwargs) + self.assertNotEqual(self.user_employee.odoobot_state, 'onboarding_attachement') + + # Test miscellaneous messages + self.user_employee.odoobot_state = "idle" + kwargs['partner_ids'] = [] + kwargs['body'] = "I love you" + self.assertNextMessage( + channel.message_post(**kwargs), + sender=self.odoobot, + answer=("too human for me",) + ) + kwargs['body'] = "Go fuck yourself" + self.assertNextMessage( + channel.message_post(**kwargs), + sender=self.odoobot, + answer=("I have feelings",) + ) + kwargs['body'] = "help me" + self.assertNextMessage( + channel.message_post(**kwargs), + sender=self.odoobot, + answer=("If you need help",) + ) + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_odoobot_no_default_answer(self): + kwargs = self.message_post_default_kwargs.copy() + kwargs.update({'body': "I'm not talking to @odoobot right now", 'partner_ids': []}) + self.assertNextMessage( + self.test_record_employe.message_post(**kwargs), + answer=False + ) + + def assertNextMessage(self, message, answer=None, sender=None): + last_message = self.env['mail.message'].search([('id', '=', message.id + 1)]) + if last_message: + body = last_message.body.replace('<p>', '').replace('</p>', '') + else: + self.assertFalse(answer, "No last message found when an answer was expect") + if answer is not None: + if answer and not last_message: + self.assertTrue(False, "No last message found") + if isinstance(answer, list): + self.assertIn(body, answer) + elif isinstance(answer, tuple): + for elem in answer: + self.assertIn(elem, body) + elif not answer: + self.assertFalse(last_message, "No answer should have been post") + return + else: + self.assertEqual(body, answer) + if sender: + self.assertEqual(sender, last_message.author_id) + return last_message diff --git a/addons/test_mail_full/tests/test_phone_blacklist.py b/addons/test_mail_full/tests/test_phone_blacklist.py new file mode 100644 index 00000000..adf37bdb --- /dev/null +++ b/addons/test_mail_full/tests/test_phone_blacklist.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestPhoneBlacklist(TestMailFullCommon, TestRecipients): + """ TODO """ + + @classmethod + def setUpClass(cls): + super(TestPhoneBlacklist, cls).setUpClass() + cls._test_body = 'VOID CONTENT' + + cls.test_record = cls.env['mail.test.sms.bl'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + 'mobile_nbr': cls.test_numbers[0], + 'phone_nbr': cls.test_numbers[1], + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + + def test_phone_blacklist_internals(self): + with self.with_user('employee'): + test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id) + self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1]) + self.assertFalse(test_record.phone_sanitized_blacklisted) + + bl_record = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}]) + test_record.invalidate_cache() + self.assertTrue(test_record.phone_sanitized_blacklisted) + + self.env['phone.blacklist'].sudo().remove(self.test_numbers_san[1]) + self.assertFalse(bl_record.active) + test_record.invalidate_cache() + self.assertFalse(test_record.phone_sanitized_blacklisted) + + self.env['phone.blacklist'].sudo().add(self.test_numbers_san[1]) + self.assertTrue(bl_record.active) + test_record.invalidate_cache() + self.assertTrue(test_record.phone_sanitized_blacklisted) + + bl_record_2 = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}]) + self.assertEqual(bl_record, bl_record_2) + + rec = self.env['mail.test.sms.bl'].search([('phone_sanitized_blacklisted', '=', True)]) + self.assertEqual(rec, test_record) + + bl_record.unlink() + rec = self.env['mail.test.sms.bl'].search([('phone_sanitized_blacklisted', '=', True)]) + self.assertEqual(rec, self.env['mail.test.sms.bl']) + + def test_phone_sanitize_api(self): + with self.with_user('employee'): + test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id) + self.assertFalse(test_record.phone_sanitized_blacklisted) + + test_record._phone_set_blacklisted() + test_record.invalidate_cache() + self.assertTrue(test_record.phone_sanitized_blacklisted) + + test_record._phone_reset_blacklisted() + test_record.invalidate_cache() + self.assertFalse(test_record.phone_sanitized_blacklisted) + + def test_phone_sanitize_internals(self): + with self.with_user('employee'): + test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id) + self.assertEqual(test_record.phone_nbr, self.test_numbers[1]) + self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1]) + + test_record.write({'phone_nbr': 'incorrect'}) + self.assertEqual(test_record.phone_nbr, 'incorrect') + self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[0]) + + test_record.write({'mobile_nbr': 'incorrect'}) + self.assertEqual(test_record.mobile_nbr, 'incorrect') + self.assertEqual(test_record.phone_sanitized, False) + + test_record.write({'phone_nbr': self.test_numbers[1]}) + self.assertEqual(test_record.phone_nbr, self.test_numbers[1]) + self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1]) diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py new file mode 100644 index 00000000..cf6b7821 --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_composer.py @@ -0,0 +1,567 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestSMSComposerComment(TestMailFullCommon, TestRecipients): + """ TODO LIST + + * add test for default_res_model / default_res_id and stuff like that; + * add test for comment put in queue; + * add test for language support (set template lang context); + * add test for sanitized / wrong numbers; + """ + + @classmethod + def setUpClass(cls): + super(TestSMSComposerComment, cls).setUpClass() + cls._test_body = 'VOID CONTENT' + + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + 'mobile_nbr': cls.test_numbers[0], + 'phone_nbr': cls.test_numbers[1], + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + + cls.sms_template = cls.env['sms.template'].create({ + 'name': 'Test Template', + 'model_id': cls.env['ir.model']._get('mail.test.sms').id, + 'body': 'Dear ${object.display_name} this is an SMS.', + }) + + def test_composer_comment_not_mail_thread(self): + with self.with_user('employee'): + record = self.env['test_performance.base'].create({'name': 'TestBase'}) + composer = self.env['sms.composer'].with_context( + active_model='test_performance.base', active_id=record.id + ).create({ + 'body': self._test_body, + 'numbers': ','.join(self.random_numbers), + }) + + with self.mockSMSGateway(): + composer._action_send_sms() + + # use sms.api directly, does not create sms.sms + self.assertNoSMS() + self.assertSMSIapSent(self.random_numbers_san, self._test_body) + + def test_composer_comment_default(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id + ).create({ + 'body': self._test_body, + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages) + + def test_composer_comment_field_1(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id, + ).create({ + 'body': self._test_body, + 'number_field_name': 'mobile_nbr', + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[0]}], self._test_body, messages) + + def test_composer_comment_field_2(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id, + ).create({ + 'body': self._test_body, + 'number_field_name': 'phone_nbr', + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages) + + def test_composer_comment_field_w_numbers(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id, + default_number_field_name='mobile_nbr', + ).create({ + 'body': self._test_body, + 'numbers': ','.join(self.random_numbers), + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertSMSNotification([ + {'partner': self.test_record.customer_id, 'number': self.test_record.mobile_nbr}, + {'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages) + + def test_composer_comment_field_w_template(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id, + default_template_id=self.sms_template.id, + default_number_field_name='mobile_nbr', + ).create({}) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_record.mobile_nbr}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages) + + def test_composer_internals(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_res_model='mail.test.sms', default_res_id=self.test_record.id, + ).create({ + 'body': self._test_body, + 'number_field_name': 'phone_nbr', + }) + + self.assertEqual(composer.res_model, self.test_record._name) + 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, 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_comment_wo_partner_wo_value_update(self): + """ Test record without partner and without phone values: should allow updating first found phone field """ + self.test_record.write({ + 'customer_id': False, + 'phone_nbr': False, + 'mobile_nbr': False, + }) + default_field_name = self.env['mail.test.sms']._sms_get_number_fields()[0] + + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + active_model='mail.test.sms', active_id=self.test_record.id, + default_composition_mode='comment', + ).create({ + 'body': self._test_body, + }) + self.assertFalse(composer.recipient_single_number_itf) + self.assertFalse(composer.recipient_single_number) + self.assertEqual(composer.number_field_name, default_field_name) + + composer.write({ + 'recipient_single_number_itf': self.random_numbers_san[0], + }) + self.assertEqual(composer.recipient_single_number_itf, self.random_numbers_san[0]) + self.assertFalse(composer.recipient_single_number) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + self.assertEqual(self.test_record[default_field_name], self.random_numbers_san[0]) + self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': self.random_numbers_san[0]}], self._test_body, messages) + + def test_composer_numbers_no_model(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='numbers' + ).create({ + 'body': self._test_body, + 'numbers': ','.join(self.random_numbers), + }) + + with self.mockSMSGateway(): + composer._action_send_sms() + + # use sms.api directly, does not create sms.sms + self.assertNoSMS() + self.assertSMSIapSent(self.random_numbers_san, self._test_body) + + +class TestSMSComposerBatch(TestMailFullCommon): + @classmethod + def setUpClass(cls): + super(TestSMSComposerBatch, cls).setUpClass() + cls._test_body = 'Zizisse an SMS.' + + cls._create_records_for_batch('mail.test.sms', 3) + cls.sms_template = cls._create_sms_template('mail.test.sms') + + def test_composer_batch_active_domain(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='comment', + default_res_model='mail.test.sms', + default_use_active_domain=True, + active_domain=[('id', 'in', self.records.ids)], + ).create({ + 'body': self._test_body, + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + for record in self.records: + self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages) + + def test_composer_batch_active_ids(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='comment', + default_res_model='mail.test.sms', + active_ids=self.records.ids + ).create({ + 'body': self._test_body, + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + for record in self.records: + self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages) + + def test_composer_batch_domain(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='comment', + default_res_model='mail.test.sms', + default_use_active_domain=True, + default_active_domain=repr([('id', 'in', self.records.ids)]), + ).create({ + 'body': self._test_body, + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + for record in self.records: + self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages) + + def test_composer_batch_res_ids(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='comment', + default_res_model='mail.test.sms', + default_res_ids=repr(self.records.ids), + ).create({ + 'body': self._test_body, + }) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + for record in self.records: + self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages) + + +class TestSMSComposerMass(TestMailFullCommon): + + @classmethod + def setUpClass(cls): + super(TestSMSComposerMass, cls).setUpClass() + cls._test_body = 'Zizisse an SMS.' + + cls._create_records_for_batch('mail.test.sms', 3) + cls.sms_template = cls._create_sms_template('mail.test.sms') + + def test_composer_mass_active_domain(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + default_use_active_domain=True, + active_domain=[('id', 'in', self.records.ids)], + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content=self._test_body) + + def test_composer_mass_active_domain_w_template(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + default_use_active_domain=True, + active_domain=[('id', 'in', self.records.ids)], + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + + def test_composer_mass_active_ids(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for partner in self.partners: + self.assertSMSOutgoing(partner, None, content=self._test_body) + + def test_composer_mass_active_ids_w_blacklist(self): + self.env['phone.blacklist'].create([{ + 'number': p.phone_sanitized, + 'active': True, + } for p in self.partners[:5]]) + + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + 'mass_use_blacklist': True, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for partner in self.partners[5:]: + self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body) + for partner in self.partners[:5]: + self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_blacklist', content=self._test_body) + + def test_composer_mass_active_ids_wo_blacklist(self): + self.env['phone.blacklist'].create([{ + 'number': p.phone_sanitized, + 'active': True, + } for p in self.partners[:5]]) + + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + 'mass_use_blacklist': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for partner in self.partners: + self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body) + + def test_composer_mass_active_ids_w_blacklist_and_done(self): + self.env['phone.blacklist'].create([{ + 'number': p.phone_sanitized, + 'active': True, + } for p in self.partners[:5]]) + for p in self.partners[8:]: + p.mobile = self.partners[8].mobile + self.assertEqual(p.phone_sanitized, self.partners[8].phone_sanitized) + + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + 'mass_use_blacklist': True, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for partner in self.partners[8:]: + self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body) + for partner in self.partners[5:8]: + self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_duplicate', content=self._test_body) + for partner in self.partners[:5]: + self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_blacklist', content=self._test_body) + + def test_composer_mass_active_ids_w_template(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + + def test_composer_mass_active_ids_w_template_and_lang(self): + self.env['res.lang']._activate_lang('fr_FR') + self.env['ir.translation'].create({ + 'type': 'model', + 'name': 'sms.template,body', + 'lang': 'fr_FR', + 'res_id': self.sms_template.id, + 'src': self.sms_template.body, + 'value': 'Cher·e· ${object.display_name} ceci est un SMS.', + }) + # set template to try to use customer lang + self.sms_template.write({ + 'lang': '${object.customer_id.lang}', + }) + # set one customer as french speaking + self.partners[2].write({'lang': 'fr_FR'}) + + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for record in self.records: + if record.customer_id == self.partners[2]: + self.assertSMSOutgoing(record.customer_id, None, content='Cher·e· %s ceci est un SMS.' % record.display_name) + else: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + + def test_composer_mass_active_ids_w_template_and_log(self): + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + active_ids=self.records.ids, + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': True, + }) + + with self.mockSMSGateway(): + composer.action_send_sms() + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + self.assertSMSLogged(record, 'Dear %s this is an SMS.' % record.display_name) + + def test_composer_template_context_action(self): + """ Test the context action from a SMS template (Add context action button) + and the usage with the sms composer """ + # Create the lang info + self.env['res.lang']._activate_lang('fr_FR') + self.env['ir.translation'].create({ + 'type': 'model', + 'name': 'sms.template,body', + 'lang': 'fr_FR', + 'res_id': self.sms_template.id, + 'src': self.sms_template.body, + 'value': "Hello ${object.display_name} ceci est en français.", + }) + # set template to try to use customer lang + self.sms_template.write({ + 'lang': '${object.customer_id.lang}', + }) + # create a second record linked to a customer in another language + self.partners[2].write({'lang': 'fr_FR'}) + test_record_2 = self.env['mail.test.sms'].create({ + 'name': 'Test', + 'customer_id': self.partners[2].id, + }) + test_record_1 = self.env['mail.test.sms'].create({ + 'name': 'Test', + 'customer_id': self.partners[1].id, + }) + # Composer creation with context from a template context action (simulate) - comment (single recipient) + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + sms_composition_mode='guess', + default_res_ids=[test_record_2.id], + default_res_id=test_record_2.id, + active_ids=[test_record_2.id], + active_id=test_record_2.id, + active_model='mail.test.sms', + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': False, + }) + self.assertEqual(composer.composition_mode, "comment") + self.assertEqual(composer.body, "Hello %s ceci est en français." % test_record_2.display_name) + + with self.mockSMSGateway(): + messages = composer._action_send_sms() + + number = self.partners[2].phone_get_sanitized_number() + self.assertSMSNotification([{'partner': test_record_2.customer_id, 'number': number}], "Hello %s ceci est en français." % test_record_2.display_name, messages) + + # Composer creation with context from a template context action (simulate) - mass (multiple recipient) + with self.with_user('employee'): + composer = self.env['sms.composer'].with_context( + sms_composition_mode='guess', + default_res_ids=[test_record_1.id, test_record_2.id], + default_res_id=test_record_1.id, + active_ids=[test_record_1.id, test_record_2.id], + active_id=test_record_1.id, + active_model='mail.test.sms', + default_template_id=self.sms_template.id, + ).create({ + 'mass_keep_log': True, + }) + self.assertEqual(composer.composition_mode, "mass") + # In english because by default but when sinding depending of record + self.assertEqual(composer.body, "Dear ${object.display_name} this is an SMS.") + + with self.mockSMSGateway(): + composer.action_send_sms() + + self.assertSMSOutgoing(test_record_1.customer_id, None, content='Dear %s this is an SMS.' % test_record_1.display_name) + self.assertSMSOutgoing(test_record_2.customer_id, None, content="Hello %s ceci est en français." % test_record_2.display_name) diff --git a/addons/test_mail_full/tests/test_sms_management.py b/addons/test_mail_full/tests/test_sms_management.py new file mode 100644 index 00000000..e0d09797 --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_management.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestSMSWizards(TestMailFullCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestSMSWizards, cls).setUpClass() + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + cls.msg = cls.test_record.message_post(body='TEST BODY', author_id=cls.partner_employee.id) + cls.notif_p1 = cls.env['mail.notification'].create({ + 'mail_message_id': cls.msg.id, + 'res_partner_id': cls.partner_1.id, + 'sms_number': cls.partner_1.mobile, + 'notification_type': 'sms', + 'notification_status': 'exception', + 'failure_type': 'sms_number_format', + }) + cls.notif_p2 = cls.env['mail.notification'].create({ + 'mail_message_id': cls.msg.id, + 'res_partner_id': cls.partner_2.id, + 'sms_number': cls.partner_2.mobile, + 'notification_type': 'sms', + 'notification_status': 'exception', + 'failure_type': 'sms_credit', + }) + + def test_sms_resend(self): + self._reset_bus() + + with self.with_user('employee'): + wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({}) + wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]}) + with self.mockSMSGateway(): + wizard.action_resend() + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'sent'}, + {'partner': self.partner_2, 'state': 'sent'} + ], 'TEST BODY', self.msg, check_sms=True) + self.assertMessageBusNotifications(self.msg) + + def test_sms_resend_update_number(self): + self._reset_bus() + + with self.with_user('employee'): + wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({}) + wizard.write({'recipient_ids': [(1, r.id, {'resend': True, 'sms_number': self.random_numbers[idx]}) for idx, r in enumerate(wizard.recipient_ids.sorted())]}) + with self.mockSMSGateway(): + wizard.action_resend() + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'sent', 'number': self.random_numbers_san[0]}, + {'partner': self.partner_2, 'state': 'sent', 'number': self.random_numbers_san[1]} + ], 'TEST BODY', self.msg, check_sms=True) + self.assertMessageBusNotifications(self.msg) + + def test_sms_resend_cancel(self): + self._reset_bus() + + with self.with_user('employee'): + wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({}) + with self.mockSMSGateway(): + wizard.action_cancel() + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'canceled', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'}, + {'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'} + ], 'TEST BODY', self.msg, check_sms=False) + self.assertMessageBusNotifications(self.msg) + + def test_sms_resend_internals(self): + self._reset_bus() + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'exception', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'}, + {'partner': self.partner_2, 'state': 'exception', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'} + ], 'TEST BODY', self.msg, check_sms=False) + + with self.with_user('employee'): + wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({}) + self.assertTrue(wizard.has_insufficient_credit) + self.assertEqual(set(wizard.mapped('recipient_ids.partner_name')), set((self.partner_1 | self.partner_2).mapped('display_name'))) + wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]}) + with self.mockSMSGateway(): + wizard.action_resend() + + def test_sms_resend_w_cancel(self): + self._reset_bus() + + with self.with_user('employee'): + wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({}) + wizard.write({'recipient_ids': [(1, r.id, {'resend': True if r.partner_id == self.partner_1 else False}) for r in wizard.recipient_ids]}) + with self.mockSMSGateway(): + wizard.action_resend() + + self.assertSMSNotification([{'partner': self.partner_1, 'state': 'sent'}], 'TEST BODY', self.msg, check_sms=True) + self.assertSMSNotification([{'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}], 'TEST BODY', self.msg, check_sms=False) + self.assertMessageBusNotifications(self.msg) + + def test_sms_cancel(self): + self._reset_bus() + + with self.mockSMSGateway(), self.with_user('employee'): + wizard = self.env['sms.cancel'].with_context(default_model=self.msg.model).create({}) + wizard.action_cancel() + + self.assertEqual((self.notif_p1 | self.notif_p2).mapped('notification_status'), ['canceled', 'canceled']) + + self.assertMessageBusNotifications(self.msg) diff --git a/addons/test_mail_full/tests/test_sms_performance.py b/addons/test_mail_full/tests/test_sms_performance.py new file mode 100644 index 00000000..cfa30321 --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_performance.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.sms.tests import common as sms_common +from odoo.addons.test_mail.tests.test_performance import BaseMailPerformance +from odoo.tests.common import users, warmup +from odoo.tests import tagged +from odoo.tools import mute_logger + + +@tagged('mail_performance') +class TestSMSPerformance(BaseMailPerformance, sms_common.SMSCase): + + def setUp(self): + super(TestSMSPerformance, self).setUp() + self.user_employee.write({ + 'login': 'employee', + 'country_id': self.env.ref('base.be').id, + }) + self.admin = self.env.user + + self.customer = self.env['res.partner'].with_context(self._quick_create_ctx).create({ + 'name': 'Test Customer', + 'email': 'test@example.com', + 'mobile': '0456123456', + 'country_id': self.env.ref('base.be').id, + }) + self.test_record = self.env['mail.test.sms'].with_context(self._quick_create_ctx).create({ + 'name': 'Test', + 'customer_id': self.customer.id, + 'phone_nbr': '0456999999', + }) + + # prepare recipients to test for more realistic workload + Partners = self.env['res.partner'].with_context(self._quick_create_ctx) + self.partners = self.env['res.partner'] + for x in range(0, 10): + self.partners |= Partners.create({ + 'name': 'Test %s' % x, + 'email': 'test%s@example.com' % x, + 'mobile': '0456%s%s0000' % (x, x), + 'country_id': self.env.ref('base.be').id, + }) + + # patch registry to simulate a ready environment + self.patch(self.env.registry, 'ready', True) + + @mute_logger('odoo.addons.sms.models.sms_sms') + @users('employee') + @warmup + def test_message_sms_record_1_partner(self): + record = self.test_record.with_user(self.env.user) + pids = self.customer.ids + with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=23): # test_mail_enterprise: 18 + messages = record._message_sms( + body='Performance Test', + partner_ids=pids, + ) + + self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>') + self.assertSMSNotification([{'partner': self.customer}], 'Performance Test', messages, sent_unlink=True) + + @mute_logger('odoo.addons.sms.models.sms_sms') + @users('employee') + @warmup + def test_message_sms_record_10_partners(self): + record = self.test_record.with_user(self.env.user) + pids = self.partners.ids + with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=41): + messages = record._message_sms( + body='Performance Test', + partner_ids=pids, + ) + + self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>') + self.assertSMSNotification([{'partner': partner} for partner in self.partners], 'Performance Test', messages, sent_unlink=True) + + @mute_logger('odoo.addons.sms.models.sms_sms') + @users('employee') + @warmup + def test_message_sms_record_default(self): + record = self.test_record.with_user(self.env.user) + with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=26): + messages = record._message_sms( + body='Performance Test', + ) + + self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>') + self.assertSMSNotification([{'partner': self.customer}], 'Performance Test', messages, sent_unlink=True) + + +@tagged('mail_performance') +class TestSMSMassPerformance(BaseMailPerformance, sms_common.MockSMS): + + def setUp(self): + super(TestSMSMassPerformance, self).setUp() + be_country_id = self.env.ref('base.be').id, + self.user_employee.write({ + 'login': 'employee', + 'country_id': be_country_id, + }) + self.admin = self.env.user + self.admin.write({ + 'country_id': be_country_id, + }) + + self._test_body = 'MASS SMS' + + records = self.env['mail.test.sms'] + partners = self.env['res.partner'] + for x in range(50): + partners += self.env['res.partner'].with_context(**self._quick_create_ctx).create({ + 'name': 'Partner_%s' % (x), + 'email': '_test_partner_%s@example.com' % (x), + 'country_id': be_country_id, + 'mobile': '047500%02d%02d' % (x, x) + }) + records += self.env['mail.test.sms'].with_context(**self._quick_create_ctx).create({ + 'name': 'Test_%s' % (x), + 'customer_id': partners[x].id, + }) + self.partners = partners + self.records = records + + self.sms_template = self.env['sms.template'].create({ + 'name': 'Test Template', + 'model_id': self.env['ir.model']._get('mail.test.sms').id, + 'body': 'Dear ${object.display_name} this is an SMS.', + }) + + @mute_logger('odoo.addons.sms.models.sms_sms') + @users('employee') + @warmup + def test_composer_mass_active_domain(self): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + default_use_active_domain=True, + active_domain=[('id', 'in', self.records.ids)], + ).create({ + 'body': self._test_body, + 'mass_keep_log': False, + }) + + with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=106): + composer.action_send_sms() + + @mute_logger('odoo.addons.sms.models.sms_sms') + @users('employee') + @warmup + def test_composer_mass_active_domain_w_log(self): + composer = self.env['sms.composer'].with_context( + default_composition_mode='mass', + default_res_model='mail.test.sms', + default_use_active_domain=True, + active_domain=[('id', 'in', self.records.ids)], + ).create({ + 'body': self._test_body, + 'mass_keep_log': True, + }) + + with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=157): + composer.action_send_sms() diff --git a/addons/test_mail_full/tests/test_sms_post.py b/addons/test_mail_full/tests/test_sms_post.py new file mode 100644 index 00000000..ad39f0c2 --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_post.py @@ -0,0 +1,437 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestSMSPost(TestMailFullCommon, TestRecipients): + """ TODO + + * add tests for new mail.message and mail.thread fields; + """ + + @classmethod + def setUpClass(cls): + super(TestSMSPost, cls).setUpClass() + cls._test_body = 'VOID CONTENT' + + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + 'mobile_nbr': cls.test_numbers[0], + 'phone_nbr': cls.test_numbers[1], + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + + def test_message_sms_internals_body(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms('<p>Mega SMS<br/>Top moumoutte</p>', partner_ids=self.partner_1.ids) + + self.assertEqual(messages.body, '<p>Mega SMS<br>Top moumoutte</p>') + self.assertEqual(messages.subtype_id, self.env.ref('mail.mt_note')) + self.assertSMSNotification([{'partner': self.partner_1}], 'Mega SMS\nTop moumoutte', messages) + + def test_message_sms_internals_check_existing(self): + with self.with_user('employee'), self.mockSMSGateway(sim_error='wrong_number_format'): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids) + + self.assertSMSNotification([{'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_number_format'}], self._test_body, messages) + + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + test_record._notify_record_by_sms(messages, {'partners': [{'id': self.partner_1.id, 'notif': 'sms'}]}, check_existing=True) + self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages) + + def test_message_sms_internals_sms_numbers(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids, sms_numbers=self.random_numbers) + + self.assertSMSNotification([{'partner': self.partner_1}, {'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages) + + def test_message_sms_internals_subtype(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms('<p>Mega SMS<br/>Top moumoutte</p>', subtype_id=self.env.ref('mail.mt_comment').id, partner_ids=self.partner_1.ids) + + self.assertEqual(messages.body, '<p>Mega SMS<br>Top moumoutte</p>') + self.assertEqual(messages.subtype_id, self.env.ref('mail.mt_comment')) + self.assertSMSNotification([{'partner': self.partner_1}], 'Mega SMS\nTop moumoutte', messages) + + def test_message_sms_internals_pid_to_number(self): + pid_to_number = { + self.partner_1.id: self.random_numbers[0], + self.partner_2.id: self.random_numbers[1], + } + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids, sms_pid_to_number=pid_to_number) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'number': self.random_numbers_san[0]}, + {'partner': self.partner_2, 'number': self.random_numbers_san[1]}], + self._test_body, messages) + + def test_message_sms_model_partner(self): + with self.with_user('employee'), self.mockSMSGateway(): + messages = self.partner_1._message_sms(self._test_body) + messages |= self.partner_2._message_sms(self._test_body) + self.assertSMSNotification([{'partner': self.partner_1}, {'partner': self.partner_2}], self._test_body, messages) + + def test_message_sms_model_partner_fallback(self): + self.partner_1.write({'mobile': False, 'phone': self.random_numbers[0]}) + + with self.mockSMSGateway(): + messages = self.partner_1._message_sms(self._test_body) + messages |= self.partner_2._message_sms(self._test_body) + + self.assertSMSNotification([{'partner': self.partner_1, 'number': self.random_numbers_san[0]}, {'partner': self.partner_2}], self._test_body, messages) + + def test_message_sms_model_w_partner_only(self): + with self.with_user('employee'): + record = self.env['mail.test.sms.partner'].create({'customer_id': self.partner_1.id}) + + with self.mockSMSGateway(): + messages = record._message_sms(self._test_body) + + self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages) + + def test_message_sms_model_w_partner_only_void(self): + with self.with_user('employee'): + record = self.env['mail.test.sms.partner'].create({'customer_id': False}) + + with self.mockSMSGateway(): + messages = record._message_sms(self._test_body) + + # should not crash but have a failed notification + self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': False, 'state': 'exception', 'failure_type': 'sms_number_missing'}], self._test_body, messages) + + def test_message_sms_model_w_partner_m2m_only(self): + with self.with_user('employee'): + record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id)]}) + + with self.mockSMSGateway(): + messages = record._message_sms(self._test_body) + + self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages) + + # TDE: should take first found one according to partner ordering + with self.with_user('employee'): + record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]}) + + with self.mockSMSGateway(): + messages = record._message_sms(self._test_body) + + self.assertSMSNotification([{'partner': self.partner_2}], self._test_body, messages) + + def test_message_sms_on_field_w_partner(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, number_field='mobile_nbr') + + self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_record.mobile_nbr}], self._test_body, messages) + + def test_message_sms_on_field_wo_partner(self): + self.test_record.write({'customer_id': False}) + + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, number_field='mobile_nbr') + + self.assertSMSNotification([{'number': self.test_record.mobile_nbr}], self._test_body, messages) + + def test_message_sms_on_field_wo_partner_wo_value(self): + """ Test record without a partner and without phone values. """ + self.test_record.write({ + 'customer_id': False, + 'phone_nbr': False, + 'mobile_nbr': False, + }) + + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body) + + # should not crash but have a failed notification + self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': False, 'state': 'exception', 'failure_type': 'sms_number_missing'}], self._test_body, messages) + + def test_message_sms_on_field_wo_partner_default_field(self): + self.test_record.write({'customer_id': False}) + + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body) + + self.assertSMSNotification([{'number': self.test_numbers_san[1]}], self._test_body, messages) + + def test_message_sms_on_field_wo_partner_default_field_2(self): + self.test_record.write({'customer_id': False, 'phone_nbr': False}) + + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body) + + self.assertSMSNotification([{'number': self.test_numbers_san[0]}], self._test_body, messages) + + def test_message_sms_on_numbers(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, sms_numbers=self.random_numbers_san) + self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages) + + def test_message_sms_on_numbers_sanitization(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, sms_numbers=self.random_numbers) + self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages) + + def test_message_sms_on_partner_ids(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids) + + self.assertSMSNotification([{'partner': self.partner_1}, {'partner': self.partner_2}], self._test_body, messages) + + def test_message_sms_on_partner_ids_default(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body) + + self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages) + + def test_message_sms_on_partner_ids_w_numbers(self): + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids, sms_numbers=self.random_numbers[:1]) + + self.assertSMSNotification([{'partner': self.partner_1}, {'number': self.random_numbers_san[0]}], self._test_body, messages) + + def test_message_sms_with_template(self): + sms_template = self.env['sms.template'].create({ + 'name': 'Test Template', + 'model_id': self.env['ir.model']._get('mail.test.sms').id, + 'body': 'Dear ${object.display_name} this is an SMS.', + }) + + with self.with_user('employee'): + with self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms_with_template(template=sms_template) + + self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages) + + def test_message_sms_with_template_fallback(self): + with self.with_user('employee'): + with self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms_with_template(template_xmlid='test_mail_full.this_should_not_exists', template_fallback='Fallback for ${object.id}') + + self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Fallback for %s' % self.test_record.id, messages) + + def test_message_sms_with_template_xmlid(self): + sms_template = self.env['sms.template'].create({ + 'name': 'Test Template', + 'model_id': self.env['ir.model']._get('mail.test.sms').id, + 'body': 'Dear ${object.display_name} this is an SMS.', + }) + self.env['ir.model.data'].create({ + 'name': 'this_should_exists', + 'module': 'test_mail_full', + 'model': sms_template._name, + 'res_id': sms_template.id, + }) + + with self.with_user('employee'): + with self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms_with_template(template_xmlid='test_mail_full.this_should_exists') + + self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages) + + +class TestSMSPostException(TestMailFullCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestSMSPostException, cls).setUpClass() + cls._test_body = 'VOID CONTENT' + + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + cls.partner_3 = cls.env['res.partner'].with_context({ + 'mail_create_nolog': True, + 'mail_create_nosubscribe': True, + 'mail_notrack': True, + 'no_reset_password': True, + }).create({ + 'name': 'Ernestine Loubine', + 'email': 'ernestine.loubine@agrolait.com', + 'country_id': cls.env.ref('base.be').id, + 'mobile': '0475556644', + }) + + def test_message_sms_w_numbers_invalid(self): + random_numbers = self.random_numbers + ['6988754'] + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, sms_numbers=random_numbers) + + # invalid numbers are still given to IAP currently as they are + self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}, {'number': random_numbers[2]}], self._test_body, messages) + + def test_message_sms_w_partners_nocountry(self): + self.test_record.customer_id.write({ + 'mobile': self.random_numbers[0], + 'phone': self.random_numbers[1], + 'country_id': False, + }) + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=self.test_record.customer_id.ids) + + self.assertSMSNotification([{'partner': self.test_record.customer_id}], self._test_body, messages) + + def test_message_sms_w_partners_falsy(self): + # TDE FIXME: currently sent to IAP + self.test_record.customer_id.write({ + 'mobile': 'youpie', + 'phone': 'youpla', + }) + with self.with_user('employee'), self.mockSMSGateway(): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=self.test_record.customer_id.ids) + + # self.assertSMSNotification({self.test_record.customer_id: {}}, {}, self._test_body, messages) + + def test_message_sms_w_numbers_sanitization_duplicate(self): + pass + # TDE FIXME: not sure + # random_numbers = self.random_numbers + [self.random_numbers[1]] + # random_numbers_san = self.random_numbers_san + [self.random_numbers_san[1]] + # with self.with_user('employee'), self.mockSMSGateway(): + # messages = self.test_record._message_sms(self._test_body, sms_numbers=random_numbers) + # self.assertSMSNotification({}, {random_numbers_san[0]: {}, random_numbers_san[1]: {}, random_numbers_san[2]: {}}, self._test_body, messages) + + def test_message_sms_crash_credit(self): + with self.with_user('employee'), self.mockSMSGateway(sim_error='credit'): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_credit'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_credit'}, + ], self._test_body, messages) + + def test_message_sms_crash_credit_single(self): + with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'credit'}): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'sent'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_credit'}, + {'partner': self.partner_3, 'state': 'sent'}, + ], self._test_body, messages) + + def test_message_sms_crash_server_crash(self): + with self.with_user('employee'), self.mockSMSGateway(sim_error='jsonrpc_exception'): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_server'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_server'}, + {'partner': self.partner_3, 'state': 'exception', 'failure_type': 'sms_server'}, + ], self._test_body, messages) + + def test_message_sms_crash_unregistered(self): + with self.with_user('employee'), self.mockSMSGateway(sim_error='unregistered'): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_acc'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_acc'}, + ], self._test_body, messages) + + def test_message_sms_crash_unregistered_single(self): + with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'unregistered'}): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'sent'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_acc'}, + {'partner': self.partner_3, 'state': 'sent'}, + ], self._test_body, messages) + + def test_message_sms_crash_wrong_number(self): + with self.with_user('employee'), self.mockSMSGateway(sim_error='wrong_number_format'): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_number_format'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_number_format'}, + ], self._test_body, messages) + + def test_message_sms_crash_wrong_number_single(self): + with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'wrong_number_format'}): + test_record = self.env['mail.test.sms'].browse(self.test_record.id) + messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids) + + self.assertSMSNotification([ + {'partner': self.partner_1, 'state': 'sent'}, + {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_number_format'}, + {'partner': self.partner_3, 'state': 'sent'}, + ], self._test_body, messages) + + +class TestSMSApi(TestMailFullCommon): + + @classmethod + def setUpClass(cls): + super(TestSMSApi, cls).setUpClass() + cls._test_body = 'Zizisse an SMS.' + + cls._create_records_for_batch('mail.test.sms', 3) + cls.sms_template = cls._create_sms_template('mail.test.sms') + + def test_message_schedule_sms(self): + with self.with_user('employee'): + with self.mockSMSGateway(): + self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=False) + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content=self._test_body) + + def test_message_schedule_sms_w_log(self): + with self.with_user('employee'): + with self.mockSMSGateway(): + self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=True) + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content=self._test_body) + self.assertSMSLogged(record, self._test_body) + + def test_message_schedule_sms_w_template(self): + with self.with_user('employee'): + with self.mockSMSGateway(): + self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=False) + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + + def test_message_schedule_sms_w_template_and_log(self): + with self.with_user('employee'): + with self.mockSMSGateway(): + self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=True) + + for record in self.records: + self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name) + self.assertSMSLogged(record, 'Dear %s this is an SMS.' % record.display_name) diff --git a/addons/test_mail_full/tests/test_sms_server_actions.py b/addons/test_mail_full/tests/test_sms_server_actions.py new file mode 100644 index 00000000..1c06123e --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_server_actions.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestServerAction(TestMailFullCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestServerAction, cls).setUpClass() + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + }) + cls.test_record_2 = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test Record 2', + 'customer_id': False, + 'phone_nbr': cls.test_numbers[0], + }) + + cls.sms_template = cls._create_sms_template('mail.test.sms') + cls.action = cls.env['ir.actions.server'].create({ + 'name': 'Test SMS Action', + 'model_id': cls.env['ir.model']._get('mail.test.sms').id, + 'state': 'sms', + 'sms_template_id': cls.sms_template.id, + 'groups_id': cls.env.ref('base.group_user'), + }) + + def test_action_sms(self): + context = { + 'active_model': 'mail.test.sms', + 'active_ids': (self.test_record | self.test_record_2).ids, + } + + with self.with_user('employee'), self.mockSMSGateway(): + self.action.with_user(self.env.user).with_context(**context).run() + + self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name) + self.assertSMSOutgoing(self.env['res.partner'], self.test_numbers_san[0], content='Dear %s this is an SMS.' % self.test_record_2.display_name) + + def test_action_sms_single(self): + context = { + 'active_model': 'mail.test.sms', + 'active_id': self.test_record.id, + } + + with self.with_user('employee'), self.mockSMSGateway(): + self.action.with_user(self.env.user).with_context(**context).run() + self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name) + + def test_action_sms_w_log(self): + self.action.sms_mass_keep_log = True + context = { + 'active_model': 'mail.test.sms', + 'active_ids': (self.test_record | self.test_record_2).ids, + } + + with self.with_user('employee'), self.mockSMSGateway(): + self.action.with_user(self.env.user).with_context(**context).run() + + self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name) + self.assertSMSLogged(self.test_record, 'Dear %s this is an SMS.' % self.test_record.display_name) + + self.assertSMSOutgoing(self.env['res.partner'], self.test_numbers_san[0], content='Dear %s this is an SMS.' % self.test_record_2.display_name) + self.assertSMSLogged(self.test_record_2, 'Dear %s this is an SMS.' % self.test_record_2.display_name) diff --git a/addons/test_mail_full/tests/test_sms_sms.py b/addons/test_mail_full/tests/test_sms_sms.py new file mode 100644 index 00000000..2c009a47 --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_sms.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import werkzeug + +from unittest.mock import patch +from unittest.mock import DEFAULT + +from odoo import exceptions +from odoo.addons.sms.models.sms_sms import SmsSms as SmsSms +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon +from odoo.tests import common + + +class LinkTrackerMock(common.BaseCase): + + def setUp(self): + super(LinkTrackerMock, self).setUp() + + def _get_title_from_url(u): + return "Test_TITLE" + + self.env['ir.config_parameter'].sudo().set_param('web.base.url', 'https://test.odoo.com') + + link_tracker_title_patch = patch('odoo.addons.link_tracker.models.link_tracker.LinkTracker._get_title_from_url', wraps=_get_title_from_url) + link_tracker_title_patch.start() + self.addCleanup(link_tracker_title_patch.stop) + + self.utm_c = self.env.ref('utm.utm_campaign_fall_drive') + self.utm_m = self.env.ref('mass_mailing_sms.utm_medium_sms') + self.tracker_values = { + 'campaign_id': self.utm_c.id, + 'medium_id': self.utm_m.id, + } + + def assertLinkTracker(self, url, url_params): + links = self.env['link.tracker'].sudo().search([('url', '=', url)]) + self.assertEqual(len(links), 1) + + # check UTMS are correctly set on redirect URL + original_url = werkzeug.urls.url_parse(url) + redirect_url = werkzeug.urls.url_parse(links.redirected_url) + redirect_params = redirect_url.decode_query().to_dict(flat=True) + self.assertEqual(redirect_url.scheme, original_url.scheme) + self.assertEqual(redirect_url.decode_netloc(), original_url.decode_netloc()) + self.assertEqual(redirect_url.path, original_url.path) + self.assertEqual(redirect_params, url_params) + + +class TestSMSPost(TestMailFullCommon, LinkTrackerMock): + + @classmethod + def setUpClass(cls): + super(TestSMSPost, cls).setUpClass() + cls._test_body = 'VOID CONTENT' + + cls.sms_all = cls.env['sms.sms'] + for x in range(10): + cls.sms_all |= cls.env['sms.sms'].create({ + 'number': '+324560000%s%s' % (x, x), + 'body': cls._test_body, + }) + + def test_body_link_shorten(self): + link = 'http://www.example.com' + self.env['link.tracker'].search([('url', '=', link)]).unlink() + new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values) + self.assertNotIn(link, new_body) + self.assertLinkTracker(link, {'utm_campaign': self.utm_c.name, 'utm_medium': self.utm_m.name}) + link = self.env['link.tracker'].search([('url', '=', link)]) + self.assertIn(link.short_url, new_body) + + link = 'https://test.odoo.com/my/super_page?test[0]=42&toto=áâà#title3' + self.env['link.tracker'].search([('url', '=', link)]).unlink() + new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values) + self.assertNotIn(link, new_body) + self.assertLinkTracker(link, { + 'utm_campaign': self.utm_c.name, + 'utm_medium': self.utm_m.name, + 'test[0]': '42', + 'toto': 'áâà', + }) + link = self.env['link.tracker'].search([('url', '=', link)]) + self.assertIn(link.short_url, new_body) + + def test_body_link_shorten_wshort(self): + link = 'https://test.odoo.com/r/RAOUL' + self.env['link.tracker'].search([('url', '=', link)]).unlink() + new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values) + self.assertIn(link, new_body) + self.assertFalse(self.env['link.tracker'].search([('url', '=', link)])) + + def test_body_link_shorten_wunsubscribe(self): + link = 'https://test.odoo.com/sms/3/' + self.env['link.tracker'].search([('url', '=', link)]).unlink() + new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values) + self.assertIn(link, new_body) + self.assertFalse(self.env['link.tracker'].search([('url', '=', link)])) + + def test_sms_body_link_shorten_suffix(self): + mailing = self.env['mailing.mailing'].create({ + 'subject': 'Minimal mailing', + 'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id, + 'mailing_type': 'sms', + }) + + sms_0 = self.env['sms.sms'].create({ + 'body': 'Welcome to https://test.odoo.com', + 'number': '12', + 'mailing_id': mailing.id, + }) + sms_1 = self.env['sms.sms'].create({ + 'body': 'Welcome to https://test.odoo.com/r/RAOUL', + 'number': '12', + }) + sms_2 = self.env['sms.sms'].create({ + 'body': 'Welcome to https://test.odoo.com/r/RAOUL', + 'number': '12', 'mailing_id': mailing.id, + }) + sms_3 = self.env['sms.sms'].create({ + 'body': 'Welcome to https://test.odoo.com/leodagan/r/RAOUL', + 'number': '12', 'mailing_id': mailing.id, + }) + + res = (sms_0 | sms_1 | sms_2 | sms_3)._update_body_short_links() + self.assertEqual(res[sms_0.id], 'Welcome to https://test.odoo.com') + self.assertEqual(res[sms_1.id], 'Welcome to https://test.odoo.com/r/RAOUL') + self.assertEqual(res[sms_2.id], 'Welcome to https://test.odoo.com/r/RAOUL/s/%s' % sms_2.id) + self.assertEqual(res[sms_3.id], 'Welcome to https://test.odoo.com/leodagan/r/RAOUL') + + def test_sms_send_batch_size(self): + self.count = 0 + + def _send(sms_self, delete_all=False, raise_exception=False): + self.count += 1 + return DEFAULT + + self.env['ir.config_parameter'].set_param('sms.session.batch.size', '3') + with patch.object(SmsSms, '_send', autospec=True, side_effect=_send) as send_mock: + self.env['sms.sms'].browse(self.sms_all.ids).send() + + self.assertEqual(self.count, 4) + + def test_sms_send_crash_employee(self): + with self.assertRaises(exceptions.AccessError): + self.env['sms.sms'].with_user(self.user_employee).browse(self.sms_all.ids).send() + + def test_sms_send_delete_all(self): + with self.mockSMSGateway(sms_allow_unlink=True, sim_error='jsonrpc_exception'): + self.env['sms.sms'].browse(self.sms_all.ids).send(delete_all=True, raise_exception=False) + self.assertFalse(len(self.sms_all.exists())) + + def test_sms_send_raise(self): + with self.assertRaises(exceptions.AccessError): + with self.mockSMSGateway(sim_error='jsonrpc_exception'): + self.env['sms.sms'].browse(self.sms_all.ids).send(raise_exception=True) + self.assertEqual(set(self.sms_all.mapped('state')), set(['outgoing'])) + + def test_sms_send_raise_catch(self): + with self.mockSMSGateway(sim_error='jsonrpc_exception'): + self.env['sms.sms'].browse(self.sms_all.ids).send(raise_exception=False) + self.assertEqual(set(self.sms_all.mapped('state')), set(['error'])) diff --git a/addons/test_mail_full/tests/test_sms_template.py b/addons/test_mail_full/tests/test_sms_template.py new file mode 100644 index 00000000..c57ae3cf --- /dev/null +++ b/addons/test_mail_full/tests/test_sms_template.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients + + +class TestSmsTemplate(TestMailFullCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestSmsTemplate, cls).setUpClass() + cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({ + 'name': 'Test', + 'customer_id': cls.partner_1.id, + }) + cls.test_record = cls._reset_mail_context(cls.test_record) + + cls.body_en = 'Dear ${object.display_name} this is an SMS.' + cls.body_fr = u"Hello ${object.display_name} ceci est en français." + cls.sms_template = cls._create_sms_template('mail.test.sms', body=cls.body_en) + + def test_sms_template_render(self): + rendered_body = self.sms_template._render_template(self.sms_template.body, self.sms_template.model, self.test_record.ids) + self.assertEqual(rendered_body[self.test_record.id], 'Dear %s this is an SMS.' % self.test_record.display_name) + + def test_sms_template_lang(self): + self.env['res.lang']._activate_lang('fr_FR') + self.user_admin.write({'lang': 'en_US'}) + self.env['ir.translation'].create({ + 'type': 'model', + 'name': 'sms.template,body', + 'lang': 'fr_FR', + 'res_id': self.sms_template.id, + 'src': self.sms_template.body, + 'value': self.body_fr, + }) + # set template to try to use customer lang + self.sms_template.write({ + 'lang': '${object.customer_id.lang}', + }) + # create a second record linked to a customer in another language + self.partner_2.write({ + 'lang': 'fr_FR', + }) + test_record_2 = self.env['mail.test.sms'].create({ + 'name': 'Test', + 'customer_id': self.partner_2.id, + }) + + self.assertEqual(self.sms_template.body, self.body_en) + self.assertEqual(self.sms_template.with_context(lang='fr_FR').body, self.body_fr) + + rid_to_lang = self.sms_template._render_lang((self.test_record | test_record_2).ids) + self.assertEqual(set(rid_to_lang.keys()), set((self.test_record | test_record_2).ids)) + for rid, lang in rid_to_lang.items(): + # TDE FIXME: False or en_US ? + if rid == self.test_record.id: + self.assertEqual(lang, 'en_US') + elif rid == test_record_2.id: + self.assertEqual(lang, 'fr_FR') + else: + self.assertTrue(False) + + tpl_to_rids = self.sms_template._classify_per_lang((self.test_record | test_record_2).ids) + for lang, (tpl, rids) in tpl_to_rids.items(): + # TDE FIXME: False or en_US ? + if lang == 'en_US': + self.assertEqual(rids, self.test_record.ids) + elif lang == 'fr_FR': + self.assertEqual(rids, test_record_2.ids) + else: + self.assertTrue(False, 'Should not return lang %s' % lang) + + def test_sms_template_create_and_unlink_sidebar_action(self): + ActWindow = self.env['ir.actions.act_window'] + self.sms_template.action_create_sidebar_action() + action_id = self.sms_template.sidebar_action_id.id + + self.assertNotEqual(action_id, False) + self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 1) + + self.sms_template.action_unlink_sidebar_action() + self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 0) + + def test_sms_template_unlink_with_action(self): + ActWindow = self.env['ir.actions.act_window'] + self.sms_template.action_create_sidebar_action() + action_id = self.sms_template.sidebar_action_id.id + + self.sms_template.unlink() + self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 0) |
