[FIX] mail: remove related mail activities on model removal

Unlinking a model during uninstallation can potentially fail when the query
removing any related mail activity types fails. This happens when such a record
is still referenced in the mail_activity table. Explicitly removing related
mail activities (as is done for the activity types and other related mail data)
resolves the issue.

Two tests are added, one that demonstrates the general flow and a regression
test triggering the behavior that caused a bug in the uninstall procedure of
the hr_holidays module.

closes odoo/odoo#104152

X-original-commit: b315f7f562fc84a09d99d6e7676567eb6f285402
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: De Caluwé Tom (tdc) <tdc@odoo.com>
This commit is contained in:
Tom De Caluwé
2022-10-26 16:07:29 +02:00
parent 886f3de768
commit c18ce5d535
5 changed files with 65 additions and 0 deletions
+1
View File
@@ -16,3 +16,4 @@ from . import test_company_leave
from . import test_res_partner
from . import test_stress_days
from . import test_global_leaves
from . import test_uninstall
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
from odoo.tests import tagged, TransactionCase
@tagged('-at_install', 'post_install')
class TestHrLeaveUninstall(TransactionCase):
def test_unlink_model(self):
model = self.env['ir.model'].search([('model', '=', 'hr.leave')])
activity_type = self.env['mail.activity'].search([
('res_model', '=', 'hr.leave')
]).activity_type_id
self.assertTrue(activity_type)
self.assertIn('hr.leave', activity_type.mapped('res_model'))
model.sudo().with_context(**{MODULE_UNINSTALL_FLAG: True}).unlink()
self.assertFalse(model.exists())
domain = [('res_model', '=', 'hr.leave')]
self.assertFalse(self.env['mail.activity'].search(domain))
self.assertFalse(self.env['mail.activity.type'].search(domain))
self.assertFalse(self.env['mail.followers'].search(domain))
self.assertFalse(self.env['mail.message'].search([
('model', '=', 'hr.leave'),
]))
+4
View File
@@ -22,6 +22,10 @@ class IrModel(models.Model):
def unlink(self):
# Delete followers, messages and attachments for models that will be unlinked.
models = tuple(self.mapped('model'))
model_ids = tuple(self.ids)
query = "DELETE FROM mail_activity WHERE res_model_id IN %s"
self.env.cr.execute(query, [model_ids])
query = "DELETE FROM mail_activity_type WHERE res_model IN %s"
self.env.cr.execute(query, [models])
+1
View File
@@ -14,4 +14,5 @@ from . import test_res_partner
from . import test_res_users
from . import test_res_users_settings
from . import test_rtc
from . import test_uninstall
from . import test_update_notification
+31
View File
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import tagged, TransactionCase
@tagged('-at_install', 'post_install')
class TestMailUninstall(TransactionCase):
def test_unlink_model(self):
model = self.env['ir.model'].create({
'name': 'Test Model',
'model': 'x_test_model',
'state': 'manual',
'is_mail_thread': True,
})
activity_type = self.env['mail.activity.type'].create({
'name': 'Test Activity Type',
'res_model': model.model,
})
record = self.env[model.model].create({})
activity = self.env['mail.activity'].create({
'activity_type_id': activity_type.id,
'res_model_id': model.id,
'res_id': record.id,
})
model.unlink()
self.assertFalse(model.exists())
self.assertFalse(activity_type.exists())
self.assertFalse(activity.exists())