diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 33a38ab5b0b..938d493b880 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -224,6 +224,7 @@ class MassMailing(models.Model): clicks_ratio = fields.Float(compute="_compute_clicks_ratio", string="Number of Clicks") next_departure = fields.Datetime(compute="_compute_next_departure", string='Scheduled date') # UX + next_departure_is_past = fields.Boolean(compute="_compute_next_departure") warning_message = fields.Char( 'Warning Message', compute='_compute_warning_message', help='Warning message displayed in the mailing form view') @@ -336,6 +337,7 @@ class MassMailing(models.Model): row['bounced_ratio'] = float_round(100.0 * row['bounced'] / total, precision_digits=2) self.browse(row.pop('mailing_id')).update(row) + @api.depends('schedule_date', 'state') def _compute_next_departure(self): # Schedule_date should only be False if schedule_type = "now" or # mass_mailing is canceled. @@ -344,12 +346,19 @@ class MassMailing(models.Model): # execute this based on the cron.trigger's call_at which should # be now() when clicking "Send" or schedule_date if scheduled + # If the departure time has passed but mailing is still in queue, this compute field + # will be used to display warning with reload button on a mailing form view. for mass_mailing in self: if mass_mailing.schedule_date: # max in case the user schedules a date in the past mass_mailing.next_departure = max(mass_mailing.schedule_date, fields.datetime.now()) else: mass_mailing.next_departure = fields.datetime.now() + past = self.filtered( + lambda mailing: mailing.state == 'in_queue' and mailing.next_departure < fields.Datetime.now() + ) + past.next_departure_is_past = True + (self - past).next_departure_is_past = False @api.depends('email_from', 'mail_server_id') def _compute_warning_message(self): @@ -621,6 +630,9 @@ class MassMailing(models.Model): self.write({'schedule_type': 'now'}) return self.action_put_in_queue() + def action_reload(self): + pass + def action_schedule(self): self.ensure_one() if self.schedule_date and self.schedule_date > fields.Datetime.now(): diff --git a/addons/mass_mailing/models/mailing_list.py b/addons/mass_mailing/models/mailing_list.py index c8ca4e4743b..ce995d2c1ed 100644 --- a/addons/mass_mailing/models/mailing_list.py +++ b/addons/mass_mailing/models/mailing_list.py @@ -160,6 +160,8 @@ class MassMailingList(models.Model): 'context': { **self.env.context, 'default_contact_list_ids': self.ids, + 'default_mailing_type': 'mail', + 'default_model_id': self.env['ir.model']._get_id('mailing.list'), }, 'target': 'current', 'view_type': 'form', diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.scss b/addons/mass_mailing/static/src/scss/mass_mailing.scss index 5a05a8b3c01..9758e674b18 100644 --- a/addons/mass_mailing/static/src/scss/mass_mailing.scss +++ b/addons/mass_mailing/static/src/scss/mass_mailing.scss @@ -54,6 +54,23 @@ color: $o-main-favorite-color; } } + // Align the model / domain container with subject field in large devices + // and provide better tap area to the filter + @include media-breakpoint-up(md) { + div[name="mailing_model_id_container"] { + width: 96%; + } + .o_mailing_filter_width { + max-width: 25%; + } + .o_readonly_modifier.o_mailing_filter_readonly_width , + &.o_form_readonly .o_mailing_filter_readonly_width { + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } } .o_xxs_form_view.o_mass_mailing_mailing_form { @@ -176,3 +193,11 @@ min-height: 55vh; } } + +.o_mass_mailing_form_full_width.o_form_editable { + .o_domain_selector.o_read_mode { + .o_domain_tree_header.oe_edit_only { + display: none; + } + } +} diff --git a/addons/mass_mailing/tests/test_mailing_list.py b/addons/mass_mailing/tests/test_mailing_list.py index 0630e8cb3ec..b9cc685d51d 100644 --- a/addons/mass_mailing/tests/test_mailing_list.py +++ b/addons/mass_mailing/tests/test_mailing_list.py @@ -119,6 +119,19 @@ class TestMailingListMerge(MassMailCommon): self.assertEqual(contact.subscription_ids[0].opt_out_datetime, datetime(2022, 1, 1, 12, 0, 0)) self.assertFalse(contact.subscription_ids[1].opt_out_datetime) + @users('user_marketing') + def test_mailing_list_action_send_mailing(self): + mailing_ctx = self.mailing_list_1.action_send_mailing().get('context', {}) + form = Form(self.env['mailing.mailing'].with_context(mailing_ctx)) + form.subject = 'Test Mail' + mailing = form.save() + # Check that mailing model and mailing list are set properly + self.assertEqual( + mailing.mailing_model_id, self.env['ir.model']._get('mailing.list'), + 'Should have correct mailing model set') + self.assertEqual(mailing.contact_list_ids, self.mailing_list_1, 'Should have correct mailing list set') + self.assertEqual(mailing.mailing_type, 'mail', 'Should have correct mailing_type') + @users('user_marketing') def test_mailing_list_contact_copy_in_context_of_mailing_list(self): MailingContact = self.env['mailing.contact'] @@ -243,6 +256,7 @@ class TestMailingContactImport(MassMailCommon): # Test that the context key "default_list_ids" is ignored (because we manually set list_ids) contact_import.with_context(default_list_ids=(first_list | second_list).ids).action_import() + self.env['mailing.list'].invalidate_model(['contact_ids']) # Check the contact of the first mailing list contacts = [ (contact.name, contact.email) diff --git a/addons/mass_mailing/views/mailing_contact_views.xml b/addons/mass_mailing/views/mailing_contact_views.xml index addad64f748..95b2dbf2b14 100644 --- a/addons/mass_mailing/views/mailing_contact_views.xml +++ b/addons/mass_mailing/views/mailing_contact_views.xml @@ -150,6 +150,7 @@ + diff --git a/addons/mass_mailing/views/mailing_list_views.xml b/addons/mass_mailing/views/mailing_list_views.xml index 30f24550672..94d387137ee 100644 --- a/addons/mass_mailing/views/mailing_list_views.xml +++ b/addons/mass_mailing/views/mailing_list_views.xml @@ -39,6 +39,10 @@ mailing.list
+
+
+
- + This mailing is scheduled for . + + + This mailing will be sent as soon as possible. + + + + + + This mailing will be sent as soon as possible. + +
@@ -194,12 +210,12 @@
-
-
+
+
-
+
-
- -
@@ -400,7 +414,7 @@ state != 'in_queue' - scheduled == 0 + not scheduled or next_departure_is_past email(s) scheduled for @@ -410,8 +424,16 @@ scheduled != 0 + + scheduled + + + scheduled + + + @@ -451,6 +473,9 @@
+ + col-md-7 + diff --git a/addons/mass_mailing/views/mailing_subscription_views.xml b/addons/mass_mailing/views/mailing_subscription_views.xml index 9c58dcf6b2f..a5dac064f5a 100644 --- a/addons/mass_mailing/views/mailing_subscription_views.xml +++ b/addons/mass_mailing/views/mailing_subscription_views.xml @@ -16,6 +16,7 @@ aria-label="Blacklisted" invisible="not is_blacklisted" groups="base.group_user">
+ @@ -32,6 +33,7 @@ + diff --git a/addons/mass_mailing/wizard/mailing_contact_import.py b/addons/mass_mailing/wizard/mailing_contact_import.py index 33693f4fbda..182b7fa56a8 100644 --- a/addons/mass_mailing/wizard/mailing_contact_import.py +++ b/addons/mass_mailing/wizard/mailing_contact_import.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models, tools, _ +from odoo import fields, models, tools, Command, _ from odoo.tools.misc import clean_context @@ -63,7 +63,10 @@ class MailingContactImport(models.TransientModel): if email not in existing_contacts: unique_contacts[email] = { 'name': name, - 'list_ids': self.mailing_list_ids.ids, + 'subscription_ids': [ + Command.create({'list_id': mailing_list_id.id}) + for mailing_list_id in self.mailing_list_ids + ], } if not unique_contacts: diff --git a/addons/mass_mailing_sms/models/mailing_list.py b/addons/mass_mailing_sms/models/mailing_list.py index 050c25870ab..8978d88d05b 100644 --- a/addons/mass_mailing_sms/models/mailing_list.py +++ b/addons/mass_mailing_sms/models/mailing_list.py @@ -27,6 +27,22 @@ class MailingList(models.Model): action['context'] = dict(action.get('context', {}), search_default_filter_valid_sms_recipient=1) return action + def action_send_mailing_sms(self): + view = self.env.ref('mass_mailing_sms.mailing_mailing_view_form_sms') + action = self.env["ir.actions.actions"]._for_xml_id('mass_mailing_sms.mailing_mailing_action_sms') + action.update({ + 'context': { + 'default_contact_list_ids': self.ids, + 'default_model_id': self.env['ir.model']._get_id('mailing.list'), + 'default_mailing_type': 'sms', + }, + 'target': 'current', + 'view_type': 'form', + 'views': [(view.id, 'form')], + }) + + return action + def _get_contact_statistics_fields(self): """ See super method docstring for more info. Adds: diff --git a/addons/mass_mailing_sms/tests/__init__.py b/addons/mass_mailing_sms/tests/__init__.py index 46cef7e2f8d..e0a3f4a6da1 100644 --- a/addons/mass_mailing_sms/tests/__init__.py +++ b/addons/mass_mailing_sms/tests/__init__.py @@ -6,3 +6,4 @@ from . import test_mailing_internals from . import test_mailing_retry from . import test_mailing_sms_ab_testing from . import test_mailing_ui +from . import test_mailing_list diff --git a/addons/mass_mailing_sms/tests/test_mailing_list.py b/addons/mass_mailing_sms/tests/test_mailing_list.py new file mode 100644 index 00000000000..f88ec5ebc3f --- /dev/null +++ b/addons/mass_mailing_sms/tests/test_mailing_list.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.mass_mailing_sms.tests.common import MassSMSCommon +from odoo.tests.common import Form, users + + +class TestMailingListSms(MassSMSCommon): + + @classmethod + def setUpClass(cls): + super(TestMailingListSms, cls).setUpClass() + cls._create_mailing_list() + + @users('user_marketing') + def test_mailing_list_action_send_sms(self): + sms_ctx = self.mailing_list_1.action_send_mailing_sms().get('context', {}) + form = Form(self.env['mailing.mailing'].with_context(sms_ctx)) + form.sms_subject = 'Test SMS' + form.body_plaintext = 'Test sms body' + sms = form.save() + # Check that mailing model and mailing list are set properly + self.assertEqual( + sms.mailing_model_id, self.env['ir.model']._get('mailing.list'), + 'Should have correct mailing model set') + self.assertEqual(sms.contact_list_ids, self.mailing_list_1, 'Should have correct mailing list set') + self.assertEqual(sms.mailing_type, 'sms', 'Should have correct mailing_type') diff --git a/addons/mass_mailing_sms/views/mailing_list_views.xml b/addons/mass_mailing_sms/views/mailing_list_views.xml index 23b9b8a945b..325e2e59769 100644 --- a/addons/mass_mailing_sms/views/mailing_list_views.xml +++ b/addons/mass_mailing_sms/views/mailing_list_views.xml @@ -29,6 +29,18 @@ + + mailing.list.view.form.inherit.sms + mailing.list + + + +