From 272359b86c030c814a1a68fca1577c57bbfbc5be Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 16 May 2019 12:17:01 +0000 Subject: [PATCH 1/2] [FIX] mail: accurate message notifications marked as sent When multiple emails (`mail.mail`) are linked to the same message (`mail.message`), all the message notifications were being marked as sent as soon as one of the mail was sent, and not all of them. This is indeed possible to have multiple emails linked to a single message, for instance when there are employees and customers in the followers of a thread, and some must therefore have the buttons meant for the employees ("Assign me") and some not. In other words, it was possible notifications were marked as sent while their associated email was not sent yet. This revision also comes as an addition to 5d6656d739feff7abb8e191fdc038cc03b542d3b as it was possible to receive a bounce coming from the previous mail sent which wrote on the same notifications `email_status` than the current message being sent before we could acquire the lock on them (see commit message of the above mentioned revision), as it was writing the `email_status` on more notifications that it should have, including notifications of the previous email. e.g - Mail 1 - Message 1 - Recipient A, B, C - Mail 2 - Message 1 - Recipient D, E Let's say recipient A bounces If the email notifying the bounce arrives during the processing of the "Mail 1", it will be blocked until we release the lock on the `mail.notification` `email_status` we acquire thanks to having written `exception` on `email_status` before sending the email. But, as soon as it is unblocked (after the `cr.commit()`), it will immediately re-attempt the delivery of the bounce email, therefore writting "bounced" in `email_status` of recipient A, while the transaction of the second email already began. When the second email sending will try to write the `email_status` of recipient A as well (as it used to write the `email_status` on all the message notifications, not just the one related to the current email), it raised a concurrent update. Therefore: 1. Interrupting the loop, as psycopg2 exceptions are raised and not passed, 2. Replay the sending of the second email a second time. opw-1984293 closes odoo/odoo#33431 Signed-off-by: Denis Ledoux --- addons/mail/models/mail_mail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index 27313fdb59d..5135503ff11 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -146,6 +146,7 @@ class MailMail(models.Model): if notif_emails: notifications = self.env['mail.notification'].search([ ('mail_message_id', 'in', notif_emails.mapped('mail_message_id').ids), + ('res_partner_id', 'in', notif_emails.mapped('recipient_ids').ids), ('is_email', '=', True)]) if mail_sent: notifications.write({ @@ -273,6 +274,7 @@ class MailMail(models.Model): notifs = self.env['mail.notification'].search([ ('is_email', '=', True), ('mail_message_id', 'in', mail.mapped('mail_message_id').ids), + ('res_partner_id', 'in', mail.mapped('recipient_ids').ids), ('email_status', 'not in', ('sent', 'canceled')) ]) if notifs: From 90d86e07dd530755cd52774932f9f613f17a3b49 Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Fri, 30 Nov 2018 13:34:09 +0000 Subject: [PATCH 2/2] [FIX] tools: allow using datetime in function call Traceback generated when trying to pass a datetime object into a function tag in xml. Used to fail. With this commit now one can pass time, datetime, timedelta, relativedelta, version, ref, pytz in function tag in xml Task-id: 1772614 Closes odoo/odoo#29212 Signed-off-by: Martin Trigaux (mat) Co-authored-by: Dhaval Limbuwala --- odoo/addons/test_convert/__init__.py | 1 + odoo/addons/test_convert/__manifest__.py | 1 + odoo/addons/test_convert/ir.model.access.csv | 2 ++ odoo/addons/test_convert/models.py | 16 ++++++++++++++ .../addons/test_convert/tests/test_convert.py | 21 +++++++++++++++++++ odoo/tools/convert.py | 7 ++++--- 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 odoo/addons/test_convert/ir.model.access.csv create mode 100644 odoo/addons/test_convert/models.py diff --git a/odoo/addons/test_convert/__init__.py b/odoo/addons/test_convert/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/odoo/addons/test_convert/__init__.py +++ b/odoo/addons/test_convert/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/odoo/addons/test_convert/__manifest__.py b/odoo/addons/test_convert/__manifest__.py index af6cd1af6e4..a9c67447615 100644 --- a/odoo/addons/test_convert/__manifest__.py +++ b/odoo/addons/test_convert/__manifest__.py @@ -5,4 +5,5 @@ 'name': 'test_convert', 'description': "Data for xml conversion tests", 'version': '0.0.1', + 'data': ['ir.model.access.csv'], } diff --git a/odoo/addons/test_convert/ir.model.access.csv b/odoo/addons/test_convert/ir.model.access.csv new file mode 100644 index 00000000000..014e4d1c827 --- /dev/null +++ b/odoo/addons/test_convert/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_test_convert_test_model,access_test_convert_test_model,model_test_convert_test_model,,1,1,1,1 \ No newline at end of file diff --git a/odoo/addons/test_convert/models.py b/odoo/addons/test_convert/models.py new file mode 100644 index 00000000000..0f059e56e86 --- /dev/null +++ b/odoo/addons/test_convert/models.py @@ -0,0 +1,16 @@ +from odoo import api, models + +class TestModel(models.Model): + _name = 'test_convert.test_model' + + @api.model + def action_test_date(self, today_date): + return True + + @api.model + def action_test_time(self, cur_time): + return True + + @api.model + def action_test_timezone(self, timezone): + return True diff --git a/odoo/addons/test_convert/tests/test_convert.py b/odoo/addons/test_convert/tests/test_convert.py index e81526dd43c..1bb79619c21 100644 --- a/odoo/addons/test_convert/tests/test_convert.py +++ b/odoo/addons/test_convert/tests/test_convert.py @@ -4,8 +4,10 @@ import collections import unittest +from lxml import etree as ET from lxml.builder import E +import odoo from odoo.tests import common from odoo.tools.convert import _eval_xml @@ -16,6 +18,25 @@ class TestEvalXML(common.TransactionCase): def eval_xml(self, node, obj=None): return _eval_xml(obj, node, self.env) + def test_function_eval(self): + def id_get(): pass + Obj = collections.namedtuple('Obj', ['module', 'idref', 'id_get']) + obj = Obj('test_convert', {}, id_get) + + try: + test_datetime = ET.XML("") + self.eval_xml(node=test_datetime, obj=obj) + test_time = ET.XML("") + self.eval_xml(node=test_time, obj=obj) + test_timedelta = ET.XML("") + self.eval_xml(node=test_timedelta, obj=obj) + test_relativedelta = ET.XML("") + self.eval_xml(node=test_relativedelta, obj=obj) + test_timezone = ET.XML("") + self.eval_xml(node=test_timezone, obj=obj) + except ValueError as e: + self.fail(e.message) + def test_char(self): self.assertEqual( self.eval_xml(Field("foo")), diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py index 284b1f783ce..4cd7693aa2c 100644 --- a/odoo/tools/convert.py +++ b/odoo/tools/convert.py @@ -177,15 +177,16 @@ def _eval_xml(self, node, env): elif node.tag == "function": args = [] a_eval = node.get('eval','') + model_str = node.get('model', '') # FIXME: should probably be exclusive if a_eval: - self.idref['ref'] = self.id_get - args = safe_eval(a_eval, self.idref) + idref2 = _get_idref(self, env, model_str, self.idref) + args = safe_eval(a_eval, idref2) for n in node: return_val = _eval_xml(self, n, env) if return_val is not None: args.append(return_val) - model = env[node.get('model', '')] + model = env[model_str] method = node.get('name') # this one still depends on the old API return odoo.api.call_kw(model, method, args, {})