diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py index cb4811b0a75..64abac84062 100644 --- a/addons/mail/__manifest__.py +++ b/addons/mail/__manifest__.py @@ -13,7 +13,6 @@ 'data/mail_groups.xml', 'wizard/mail_blacklist_remove_views.xml', 'wizard/mail_compose_message_views.xml', - 'wizard/mail_resend_cancel_views.xml', 'wizard/mail_resend_message_views.xml', 'wizard/mail_template_preview_views.xml', 'wizard/mail_wizard_invite_views.xml', diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index aea4cd8e184..4390c6fc82b 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -2105,6 +2105,48 @@ class MailThread(models.AbstractModel): # NOTIFICATION API # ------------------------------------------------------ + def _notify_cancel_by_type_generic(self, notification_type): + """ Standard implementation for canceling notifications by type that cancels notifications + * in 'bounce' and 'exception' status + * of the current user + * of the given type + * for mail_message related to the model implemented by this class + It also sends bus notifications to update status of notifications in the web client. + """ + author_id = self.env.user.partner_id.id + self._cr.execute(""" + SELECT notif.id, msg.id + FROM mail_notification notif + JOIN mail_message msg ON notif.mail_message_id = msg.id + WHERE notif.notification_type = %(notification_type)s + AND notif.author_id = %(author_id)s + AND notif.notification_status IN ('bounce', 'exception') + AND msg.model = %(model_name)s + """, {'model_name': self._name, 'author_id': author_id, 'notification_type': notification_type}) + records = self._cr.fetchall() + if records: + notif_ids, msg_ids = zip(*records) + msg_ids = list(set(msg_ids)) + if notif_ids: + self.env['mail.notification'].browse(notif_ids).sudo().write({'notification_status': 'canceled'}) + if msg_ids: + self.env['mail.message'].browse(msg_ids)._notify_message_notification_update() + return True + + @api.model + def notify_cancel_by_type(self, notification_type): + """ Subclasses must call this method and then + * either call the standard implementation _notify_cancel_by_type_generic + * or implements their own logic + """ + if not self.env.user.has_group('base.group_user'): + raise exceptions.AccessError(_("Access Denied")) + self.check_access_rights('read') + + if notification_type == 'email': + self._notify_cancel_by_type_generic('email') + return True + def _notify_thread(self, message, msg_vals=False, **kwargs): """ Main notification method. This method basically does two things diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 68dfa6872fd..33fefed4c3d 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -45,7 +45,6 @@ access_mail_blacklist_system,access_mail_blacklist_system,model_mail_blacklist,b access_mail_wizard_invite,access.mail.wizard.invite,model_mail_wizard_invite,base.group_user,1,1,1,0 access_mail_compose_message,access.mail.compose.message,model_mail_compose_message,base.group_user,1,1,1,0 access_mail_compose_message_portal,access.mail.compose.message.portal,model_mail_compose_message,base.group_portal,1,1,1,0 -access_mail_resend_cancel,access.mail.resend.cancel,model_mail_resend_cancel,base.group_user,1,1,1,0 access_mail_resend_message,access.mail.resend.message,model_mail_resend_message,base.group_user,1,1,1,0 access_mail_resend_partner,access.mail.resend.partner,model_mail_resend_partner,base.group_user,1,1,1,0 access_mail_template_preview,access.mail.template.preview,model_mail_template_preview,base.group_user,1,1,1,0 diff --git a/addons/mail/static/src/components/notification_group/notification_group.js b/addons/mail/static/src/components/notification_group/notification_group.js index 3ec1ef1ca34..1049ef85f6c 100644 --- a/addons/mail/static/src/components/notification_group/notification_group.js +++ b/addons/mail/static/src/components/notification_group/notification_group.js @@ -63,10 +63,7 @@ export class NotificationGroup extends Component { * @param {MouseEvent} ev */ _onClickMarkAsRead(ev) { - this.notificationGroupView.notificationGroup.openCancelAction(); - if (!this.messaging.device.isMobile) { - this.messaging.messagingMenu.close(); - } + this.notificationGroupView.notificationGroup.notifyCancel(); } } diff --git a/addons/mail/static/src/models/notification_group.js b/addons/mail/static/src/models/notification_group.js index 26c92a9eea1..f9342e9ec44 100644 --- a/addons/mail/static/src/models/notification_group.js +++ b/addons/mail/static/src/models/notification_group.js @@ -10,20 +10,13 @@ registerModel({ identifyingFields: ['res_model', 'res_id', 'notification_type'], recordMethods: { /** - * Opens the view that allows to cancel all notifications of the group. + * Cancel notifications of the group. */ - openCancelAction() { - if (this.notification_type !== 'email') { - return; - } - this.env.bus.trigger('do-action', { - action: 'mail.mail_resend_cancel_action', - options: { - additional_context: { - default_model: this.res_model, - unread_counter: this.notifications.length, - }, - }, + notifyCancel() { + this.env.services.rpc({ + model: this.res_model, + method: 'notify_cancel_by_type', + kwargs: { notification_type: this.notification_type }, }); }, /** diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 729adabd337..de2921ac5cd 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -301,6 +301,9 @@ MockServer.include({ delete kwargs.context; return this._mockMailThreadMessagePost(args.model, [id], kwargs, context); } + if (args.method === 'notify_cancel_by_type') { + return this._mockMailThreadNotifyCancelByType(args.model, args.kwargs.notification_type); + } return this._super(route, args); }, /** @@ -2364,4 +2367,31 @@ MockServer.include({ starred_counter: this._getRecords('mail.message', [['starred_partner_ids', 'in', user.partner_id]]).length, }; }, + /** + * Simulate the `notify_cancel_by_type` on `mail.thread` . + * Note that this method is overridden by snailmail module but not simulated here. + */ + _mockMailThreadNotifyCancelByType(model, notificationType) { + // Query matching notifications + const notifications = this._getRecords('mail.notification', [ + ['notification_type', '=', notificationType], + ['notification_status', 'in', ['bounce', 'exception']], + ]).filter(notification => { + const message = this._getRecords('mail.message', [['id', '=', notification.mail_message_id]])[0]; + return message.model === model && message.author_id === this.currentPartnerId; + }); + // Update notification status + this._mockWrite('mail.notification', [ + notifications.map(notification => notification.id), + { notification_status: 'canceled' }, + ]); + // Send bus notifications to update status of notifications in the web client + this._widget.call('bus_service', 'trigger', 'notification', [{ + payload: { + elements: this._mockMailMessage_MessageNotificationFormat( + notifications.map(notification => notification.mail_message_id)), + }, + type: 'mail.message/notification_update', + }]); + }, }); diff --git a/addons/mail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js b/addons/mail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js index 3fc560a5379..0f6980297a0 100644 --- a/addons/mail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js @@ -86,7 +86,7 @@ QUnit.test('notification group basic layout', async function (assert) { }); QUnit.test('mark as read', async function (assert) { - assert.expect(6); + assert.expect(2); const pyEnv = await startServer(); const mailChannelId1 = pyEnv['mail.channel'].create(); @@ -100,28 +100,9 @@ QUnit.test('mark as read', async function (assert) { pyEnv['mail.notification'].create({ mail_message_id: mailMessageId1, // id of the related message notification_status: 'exception', // necessary value to have a failure - notification_type: 'email', // expected failure type for email message + notification_type: 'email', }); - const bus = new Bus(); - bus.on('do-action', null, payload => { - assert.step('do_action'); - assert.strictEqual( - payload.action, - 'mail.mail_resend_cancel_action', - "action should be the one to cancel email" - ); - assert.strictEqual( - payload.options.additional_context.default_model, - 'mail.channel', - "action should have the group model as default_model" - ); - assert.strictEqual( - payload.options.additional_context.unread_counter, - 1, - "action should have the group notification length as unread_counter" - ); - }); - const { createNotificationListComponent } = await start({ env: { bus } }); + const { createNotificationListComponent } = await start(); await createNotificationListComponent(); assert.containsOnce( document.body, @@ -129,10 +110,13 @@ QUnit.test('mark as read', async function (assert) { "should have 1 mark as read button" ); - document.querySelector('.o_NotificationGroup_markAsRead').click(); - assert.verifySteps( - ['do_action'], - "should do an action to display the cancel email dialog" + await afterNextRender(() => { + document.querySelector('.o_NotificationGroup_markAsRead').click(); + }); + assert.containsNone( + document.body, + '.o_NotificationGroup', + "should have no notification group" ); }); diff --git a/addons/mail/wizard/__init__.py b/addons/mail/wizard/__init__.py index c5e1916d46d..c3791d014a6 100644 --- a/addons/mail/wizard/__init__.py +++ b/addons/mail/wizard/__init__.py @@ -5,7 +5,6 @@ from . import base_module_uninstall from . import base_partner_merge_automatic_wizard from . import mail_blacklist_remove from . import mail_compose_message -from . import mail_resend_cancel from . import mail_resend_message from . import mail_template_preview from . import mail_wizard_invite diff --git a/addons/mail/wizard/mail_resend_cancel.py b/addons/mail/wizard/mail_resend_cancel.py deleted file mode 100644 index 3d454694b9e..00000000000 --- a/addons/mail/wizard/mail_resend_cancel.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import _, api, fields, models - - -class MailResendCancel(models.TransientModel): - _name = 'mail.resend.cancel' - _description = 'Dismiss notification for resend by model' - - model = fields.Char(string='Model') - help_message = fields.Char(string='Help message', compute='_compute_help_message') - - @api.depends('model') - def _compute_help_message(self): - for wizard in self: - wizard.help_message = _("Are you sure you want to discard %s mail delivery failures? You won't be able to re-send these mails later!") % (wizard._context.get('unread_counter')) - - def cancel_resend_action(self): - author_id = self.env.user.partner_id.id - for wizard in self: - self._cr.execute(""" - SELECT notif.id, mes.id - FROM mail_notification notif - JOIN mail_message mes - ON notif.mail_message_id = mes.id - WHERE notif.notification_type = 'email' - AND notif.author_id = %(author_id)s - AND notif.notification_status IN ('bounce', 'exception') - AND mes.model = %(model_name)s - """, {'model_name': wizard.model, 'author_id': author_id}) - res = self._cr.fetchall() - notif_ids = [row[0] for row in res] - messages_ids = list(set([row[1] for row in res])) - if notif_ids: - self.env["mail.notification"].browse(notif_ids).sudo().write({'notification_status': 'canceled'}) - self.env["mail.message"].browse(messages_ids)._notify_message_notification_update() - return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mail/wizard/mail_resend_cancel_views.xml b/addons/mail/wizard/mail_resend_cancel_views.xml deleted file mode 100644 index 83b2d02cc01..00000000000 --- a/addons/mail/wizard/mail_resend_cancel_views.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - mail.resend.cancel.view.form - mail.resend.cancel - - - - - - If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red envelope next to each message. - - - - - - - Discard mail delivery failures - mail.resend.cancel - ir.actions.act_window - form - new - - diff --git a/addons/sms/__manifest__.py b/addons/sms/__manifest__.py index c8ffc63f7bb..9252e676299 100644 --- a/addons/sms/__manifest__.py +++ b/addons/sms/__manifest__.py @@ -3,7 +3,7 @@ { 'name': 'SMS gateway', - 'version': '2.2', + 'version': '2.3', 'category': 'Hidden/Tools', 'summary': 'SMS Text Messaging', 'description': """ @@ -20,7 +20,6 @@ The service is provided by the In App Purchase Odoo platform. ], 'data': [ 'data/ir_cron_data.xml', - 'wizard/sms_cancel_views.xml', 'wizard/sms_composer_views.xml', 'wizard/sms_template_preview_views.xml', 'wizard/sms_resend_views.xml', diff --git a/addons/sms/data/mail_demo.xml b/addons/sms/data/mail_demo.xml index 079c1bcec24..9a007a0066b 100644 --- a/addons/sms/data/mail_demo.xml +++ b/addons/sms/data/mail_demo.xml @@ -19,6 +19,7 @@ + email @@ -45,6 +46,7 @@ + sms @@ -61,6 +63,7 @@ + sms @@ -76,6 +79,7 @@ + sms diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py index 3230d41d20a..136dbcd46b6 100644 --- a/addons/sms/models/mail_thread.py +++ b/addons/sms/models/mail_thread.py @@ -362,3 +362,11 @@ class MailThread(models.AbstractModel): sms_all.filtered(lambda sms: sms.state == 'outgoing').send(auto_commit=False, raise_exception=False) return True + + @api.model + def notify_cancel_by_type(self, notification_type): + super().notify_cancel_by_type(notification_type) + if notification_type == 'sms': + # TDE CHECK: delete pending SMS + self._notify_cancel_by_type_generic('sms') + return True diff --git a/addons/sms/security/ir.model.access.csv b/addons/sms/security/ir.model.access.csv index bf54348054c..cdda764ff8d 100644 --- a/addons/sms/security/ir.model.access.csv +++ b/addons/sms/security/ir.model.access.csv @@ -4,7 +4,6 @@ access_sms_sms_system,access.sms.sms.system,model_sms_sms,base.group_system,1,1, access_sms_template_all,access.sms.template.all,model_sms_template,,0,0,0,0 access_sms_template_user,access.sms.template.user,model_sms_template,base.group_user,1,0,0,0 access_sms_template_system,access.sms.template.system,model_sms_template,base.group_system,1,1,1,1 -access_sms_cancel,access.sms.cancel,model_sms_cancel,base.group_user,1,1,1,0 access_sms_composer,access.sms.composer,model_sms_composer,base.group_user,1,1,1,0 access_sms_resend_recipient,access.sms.resend.recipient,model_sms_resend_recipient,base.group_user,1,1,1,0 access_sms_resend,access.sms.resend,model_sms_resend,base.group_user,1,1,1,0 diff --git a/addons/sms/static/src/models/notification_group.js b/addons/sms/static/src/models/notification_group.js index c4a58b75a3a..4212941ded4 100644 --- a/addons/sms/static/src/models/notification_group.js +++ b/addons/sms/static/src/models/notification_group.js @@ -5,23 +5,6 @@ import { patchRecordMethods } from '@mail/model/model_core'; import '@mail/models/notification_group'; patchRecordMethods('NotificationGroup', { - /** - * @override - */ - openCancelAction() { - if (this.notification_type !== 'sms') { - return this._super(...arguments); - } - this.env.bus.trigger('do-action', { - action: 'sms.sms_cancel_action', - options: { - additional_context: { - default_model: this.res_model, - unread_counter: this.notifications.length, - }, - }, - }); - }, /** * @override */ diff --git a/addons/sms/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js b/addons/sms/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js index 88f2c09a908..ba11e68cc76 100644 --- a/addons/sms/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js +++ b/addons/sms/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js @@ -14,15 +14,16 @@ QUnit.module('notification_list_notification_group_tests.js', { }); QUnit.test('mark as read', async function (assert) { - assert.expect(6); + assert.expect(2); this.data['mail.message'].records.push( // message that is expected to have a failure { - id: 11, // random unique id, will be used to link failure to message - message_type: 'sms', // message must be sms (goal of the test) - model: 'mail.channel', // expected value to link message to channel - res_id: 31, // id of a random channel + author_id: this.data.currentPartnerId, + id: 11, + message_type: 'sms', + model: 'mail.channel', + res_id: 31, } ); this.data['mail.notification'].records.push( @@ -30,42 +31,24 @@ QUnit.test('mark as read', async function (assert) { { mail_message_id: 11, // id of the related message notification_status: 'exception', // necessary value to have a failure - notification_type: 'sms', // expected failure type for sms message + notification_type: 'sms', } ); - const bus = new Bus(); - bus.on('do-action', null, payload => { - assert.step('do_action'); - assert.strictEqual( - payload.action, - 'sms.sms_cancel_action', - "action should be the one to cancel sms" - ); - assert.strictEqual( - payload.options.additional_context.default_model, - 'mail.channel', - "action should have the group model as default_model" - ); - assert.strictEqual( - payload.options.additional_context.unread_counter, - 1, - "action should have the group notification length as unread_counter" - ); - }); - - const { createNotificationListComponent } = await start({ data: this.data, env: { bus } }); + const { afterNextRender, createNotificationListComponent } = await start({ data: this.data }); await createNotificationListComponent(); - assert.containsOnce( document.body, '.o_NotificationGroup_markAsRead', "should have 1 mark as read button" ); - document.querySelector('.o_NotificationGroup_markAsRead').click(); - assert.verifySteps( - ['do_action'], - "should do an action to display the cancel sms dialog" + await afterNextRender(() => { + document.querySelector('.o_NotificationGroup_markAsRead').click(); + }); + assert.containsNone( + document.body, + '.o_NotificationGroup', + "should have no notification group" ); }); diff --git a/addons/sms/wizard/__init__.py b/addons/sms/wizard/__init__.py index bd0a5c11647..950892f0468 100644 --- a/addons/sms/wizard/__init__.py +++ b/addons/sms/wizard/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from . import sms_cancel from . import sms_composer from . import sms_resend from . import sms_template_preview diff --git a/addons/sms/wizard/sms_cancel.py b/addons/sms/wizard/sms_cancel.py deleted file mode 100644 index ab75c2c6873..00000000000 --- a/addons/sms/wizard/sms_cancel.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import _, api, fields, models - - -class SMSCancel(models.TransientModel): - _name = 'sms.cancel' - _description = 'Dismiss notification for resend by model' - - model = fields.Char(string='Model', required=True) - help_message = fields.Char(string='Help message', compute='_compute_help_message') - - @api.depends('model') - def _compute_help_message(self): - for wizard in self: - wizard.help_message = _("Are you sure you want to discard %s SMS delivery failures? You won't be able to re-send these SMS later!") % (wizard._context.get('unread_counter')) - - def action_cancel(self): - # TDE CHECK: delete pending SMS - author_id = self.env.user.partner_id.id - for wizard in self: - self._cr.execute(""" -SELECT notif.id, msg.id -FROM mail_notification notif -JOIN mail_message msg - ON notif.mail_message_id = msg.id -WHERE notif.notification_type = 'sms' IS TRUE AND notif.notification_status IN ('bounce', 'exception') - AND msg.model = %s - AND msg.author_id = %s """, (wizard.model, author_id)) - res = self._cr.fetchall() - notif_ids = [row[0] for row in res] - message_ids = list(set([row[1] for row in res])) - if notif_ids: - self.env['mail.notification'].browse(notif_ids).sudo().write({'notification_status': 'canceled'}) - if message_ids: - self.env['mail.message'].browse(message_ids)._notify_message_notification_update() - return {'type': 'ir.actions.act_window_close'} diff --git a/addons/sms/wizard/sms_cancel_views.xml b/addons/sms/wizard/sms_cancel_views.xml deleted file mode 100644 index 31f9fa8fcba..00000000000 --- a/addons/sms/wizard/sms_cancel_views.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - sms.cancel.form - sms.cancel - - - - - - If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red icon next to each message. - - - - - - - Discard SMS delivery failures - sms.cancel - ir.actions.act_window - form - new - - diff --git a/addons/snailmail/__manifest__.py b/addons/snailmail/__manifest__.py index 6b6cf33e515..1234223b99d 100644 --- a/addons/snailmail/__manifest__.py +++ b/addons/snailmail/__manifest__.py @@ -6,7 +6,7 @@ Allows users to send documents by post ===================================================== """, 'category': 'Hidden/Tools', - 'version': '0.3', + 'version': '0.4', 'depends': [ 'iap_mail', 'mail' @@ -16,7 +16,6 @@ Allows users to send documents by post 'views/report_assets.xml', 'views/snailmail_views.xml', 'wizard/snailmail_confirm_views.xml', - 'wizard/snailmail_letter_cancel_views.xml', 'wizard/snailmail_letter_format_error_views.xml', 'wizard/snailmail_letter_missing_required_fields_views.xml', 'security/ir.model.access.csv', diff --git a/addons/snailmail/models/__init__.py b/addons/snailmail/models/__init__.py index 3c7ec4a947b..da01330dbf0 100644 --- a/addons/snailmail/models/__init__.py +++ b/addons/snailmail/models/__init__.py @@ -8,3 +8,4 @@ from . import res_company from . import res_config_settings from . import res_partner from . import snailmail_letter +from . import mail_thread diff --git a/addons/snailmail/models/mail_thread.py b/addons/snailmail/models/mail_thread.py new file mode 100644 index 00000000000..8b72bb999c2 --- /dev/null +++ b/addons/snailmail/models/mail_thread.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models +from odoo import api + + +class MailThread(models.AbstractModel): + _inherit = 'mail.thread' + + def _notify_cancel_snail(self): + author_id = self.env.user.id + letters = self.env['snailmail.letter'].search([ + ('state', 'not in', ['sent', 'canceled', 'pending']), + ('user_id', '=', author_id), + ('model', '=', self._name) + ]) + letters.cancel() + + @api.model + def notify_cancel_by_type(self, notification_type): + super().notify_cancel_by_type(notification_type) + if notification_type == 'snail': + self._notify_cancel_snail() + return True diff --git a/addons/snailmail/security/ir.model.access.csv b/addons/snailmail/security/ir.model.access.csv index 3472196ad25..63c55aacfe7 100644 --- a/addons/snailmail/security/ir.model.access.csv +++ b/addons/snailmail/security/ir.model.access.csv @@ -1,6 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_snailmail_letter_user,snailmail.letter.user,model_snailmail_letter,base.group_user,1,1,1,0 access_snailmail_letter_system,snailmail.letter.system,model_snailmail_letter,base.group_system,1,1,1,1 -access_snailmail_letter_cancel,access.snailmail.letter.cancel,model_snailmail_letter_cancel,base.group_user,1,1,1,0 access_snailmail_letter_format_error,access.snailmail.letter.format.error,model_snailmail_letter_format_error,base.group_user,1,1,1,0 access_snailmail_letter_missing_required_fields,access.snailmail.letter.missing.required.fields,model_snailmail_letter_missing_required_fields,base.group_user,1,1,1,0 diff --git a/addons/snailmail/static/src/models/notification_group.js b/addons/snailmail/static/src/models/notification_group.js index 736c446e84f..e2ee613a832 100644 --- a/addons/snailmail/static/src/models/notification_group.js +++ b/addons/snailmail/static/src/models/notification_group.js @@ -5,23 +5,6 @@ import { patchRecordMethods } from '@mail/model/model_core'; import '@mail/models/notification_group'; patchRecordMethods('NotificationGroup', { - /** - * @override - */ - openCancelAction() { - if (this.notification_type !== 'snail') { - return this._super(...arguments); - } - this.env.bus.trigger('do-action', { - action: 'snailmail.snailmail_letter_cancel_action', - options: { - additional_context: { - default_model: this.res_model, - unread_counter: this.notifications.length, - }, - }, - }); - }, /** * @override */ diff --git a/addons/snailmail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js b/addons/snailmail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js index 9bea11a000d..8187d1d4f17 100644 --- a/addons/snailmail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js +++ b/addons/snailmail/static/tests/qunit_suite_tests/components/notification_list_notification_group_tests.js @@ -13,53 +13,41 @@ QUnit.module('notification_list_notification_group_tests.js', { }); QUnit.test('mark as read', async function (assert) { - assert.expect(6); + assert.expect(2); + + // Note: The server code is too complex to be rewritten in javascript. + // Actually, the server rely on the model "snailmail.letter" to identify the notification to cancel + // The following code simulates the cancel of the notification without using "snailmail.letter" model // message that is expected to have a failure this.data['mail.message'].records.push({ - id: 11, // random unique id, will be used to link failure to message - message_type: 'snailmail', // message must be snailmail (goal of the test) - model: 'mail.channel', // expected value to link message to channel - res_id: 31, // id of a random channel + author_id: this.data.currentPartnerId, + id: 11, + message_type: 'snailmail', + model: 'mail.channel', + res_id: 31, }); // failure that is expected to be used in the test this.data['mail.notification'].records.push({ mail_message_id: 11, // id of the related message notification_status: 'exception', // necessary value to have a failure - notification_type: 'snail', // expected failure type for snailmail message + notification_type: 'snail', }); - const bus = new Bus(); - bus.on('do-action', null, payload => { - assert.step('do_action'); - assert.strictEqual( - payload.action, - 'snailmail.snailmail_letter_cancel_action', - "action should be the one to cancel letter" - ); - assert.strictEqual( - payload.options.additional_context.default_model, - 'mail.channel', - "action should have the group model as default_model" - ); - assert.strictEqual( - payload.options.additional_context.unread_counter, - 1, - "action should have the group notification length as unread_counter" - ); - }); - const { createNotificationListComponent } = await start({ data: this.data, env: { bus } }); + const { afterNextRender, createNotificationListComponent } = await start({ data: this.data }); await createNotificationListComponent(); - assert.containsOnce( document.body, '.o_NotificationGroup_markAsRead', "should have 1 mark as read button" ); - document.querySelector('.o_NotificationGroup_markAsRead').click(); - assert.verifySteps( - ['do_action'], - "should do an action to display the cancel letter dialog" + await afterNextRender(() => { + document.querySelector('.o_NotificationGroup_markAsRead').click(); + }); + assert.containsNone( + document.body, + '.o_NotificationGroup', + "should have no notification group" ); }); diff --git a/addons/snailmail/wizard/__init__.py b/addons/snailmail/wizard/__init__.py index de2f4be2c8c..3e4ec78d4c0 100644 --- a/addons/snailmail/wizard/__init__.py +++ b/addons/snailmail/wizard/__init__.py @@ -1,4 +1,3 @@ from . import snailmail_confirm -from . import snailmail_letter_cancel from . import snailmail_letter_format_error from . import snailmail_letter_missing_required_fields diff --git a/addons/snailmail/wizard/snailmail_letter_cancel.py b/addons/snailmail/wizard/snailmail_letter_cancel.py deleted file mode 100644 index 59d55325124..00000000000 --- a/addons/snailmail/wizard/snailmail_letter_cancel.py +++ /dev/null @@ -1,26 +0,0 @@ - -from odoo import _, api, fields, models - -class SnailmailLetterCancel(models.TransientModel): - _name = 'snailmail.letter.cancel' - _description = 'Dismiss notification for resend by model' - - model = fields.Char(string='Model') - help_message = fields.Char(string='Help message', compute='_compute_help_message') - - @api.depends('model') - def _compute_help_message(self): - for wizard in self: - wizard.help_message = _("Are you sure you want to discard %s snailmail delivery failures? You won't be able to re-send these letters later!") % (wizard._context.get('unread_counter')) - - def cancel_resend_action(self): - author_id = self.env.user.id - for wizard in self: - letters = self.env['snailmail.letter'].search([ - ('state', 'not in', ['sent', 'canceled', 'pending']), - ('user_id', '=', author_id), - ('model', '=', wizard.model) - ]) - for letter in letters: - letter.cancel() - return {'type': 'ir.actions.act_window_close'} diff --git a/addons/snailmail/wizard/snailmail_letter_cancel_views.xml b/addons/snailmail/wizard/snailmail_letter_cancel_views.xml deleted file mode 100644 index bff8492a84f..00000000000 --- a/addons/snailmail/wizard/snailmail_letter_cancel_views.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - snailmail.letter.cancel.form - snailmail.letter.cancel - - - - - - If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red paper-plane next to each message. - - - - - - - Discard snailmail delivery failures - snailmail.letter.cancel - ir.actions.act_window - form - new - - diff --git a/addons/test_mail/tests/__init__.py b/addons/test_mail/tests/__init__.py index 8d34da5957e..bccb72c373e 100644 --- a/addons/test_mail/tests/__init__.py +++ b/addons/test_mail/tests/__init__.py @@ -18,3 +18,4 @@ from . import test_message_post from . import test_message_track from . import test_performance from . import test_ui +from . import test_mail_management diff --git a/addons/test_mail/tests/test_mail_management.py b/addons/test_mail/tests/test_mail_management.py new file mode 100644 index 00000000000..c80f57e0a99 --- /dev/null +++ b/addons/test_mail/tests/test_mail_management.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients +from odoo.tests import tagged + + +@tagged('mail_management') +class TestMailManagement(TestMailCommon, TestRecipients): + + @classmethod + def setUpClass(cls): + super(TestMailManagement, cls).setUpClass() + cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test'}) + 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({ + 'author_id': cls.msg.author_id.id, + 'mail_message_id': cls.msg.id, + 'res_partner_id': cls.partner_1.id, + 'notification_type': 'email', + 'notification_status': 'exception', + 'failure_type': 'mail_smtp', + }) + cls.notif_p2 = cls.env['mail.notification'].create({ + 'author_id': cls.msg.author_id.id, + 'mail_message_id': cls.msg.id, + 'res_partner_id': cls.partner_2.id, + 'notification_type': 'email', + 'notification_status': 'bounce', + 'failure_type': 'unknown', + }) + cls.partner_3 = cls.env['res.partner'].create({ + 'name': 'Partner3', + 'email': 'partner3@example.com', + }) + cls.notif_p3 = cls.env['mail.notification'].create({ + 'author_id': cls.msg.author_id.id, + 'mail_message_id': cls.msg.id, + 'res_partner_id': cls.partner_3.id, + 'notification_type': 'email', + 'notification_status': 'sent', + 'failure_type': None, + }) + + def test_mail_notify_cancel(self): + self._reset_bus() + + self.test_record.with_user(self.user_employee).notify_cancel_by_type('email') + self.assertEqual((self.notif_p1 | self.notif_p2 | self.notif_p3).mapped('notification_status'), + ['canceled', 'canceled', 'sent']) + + self.assertMessageBusNotifications(self.msg) diff --git a/addons/test_mail_full/tests/test_sms_management.py b/addons/test_mail_full/tests/test_sms_management.py index 4c23005bafb..cffcbc733cc 100644 --- a/addons/test_mail_full/tests/test_sms_management.py +++ b/addons/test_mail_full/tests/test_sms_management.py @@ -58,6 +58,15 @@ class TestSMSActionsCommon(TestMailFullCommon, TestMailFullRecipients): @tagged('sms_management') class TestSMSActions(TestSMSActionsCommon): + def test_sms_notify_cancel(self): + self._reset_bus() + + with self.with_user('employee'): + self.test_record.with_user(self.env.user).notify_cancel_by_type('sms') + self.assertEqual((self.notif_p1 | self.notif_p2).mapped('notification_status'), ['canceled', 'canceled']) + + self.assertMessageBusNotifications(self.msg) + def test_sms_set_cancel(self): self._reset_bus() self.sms_p1.action_set_canceled() @@ -188,14 +197,3 @@ class TestSMSWizards(TestSMSActionsCommon): 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)
If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red envelope next to each message.
If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red icon next to each message.
If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red paper-plane next to each message.