Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
2
tracking_manager/tests/__init__.py
Executable file
2
tracking_manager/tests/__init__.py
Executable file
@@ -0,0 +1,2 @@
|
||||
from . import test_tracking_manager
|
||||
from . import test_mail_tracking_value
|
||||
183
tracking_manager/tests/test_mail_tracking_value.py
Executable file
183
tracking_manager/tests/test_mail_tracking_value.py
Executable file
@@ -0,0 +1,183 @@
|
||||
# Copyright 2025 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo.addons.base.tests.common import BaseCommon
|
||||
|
||||
|
||||
class TestMailTracking(BaseCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.MailTracking = cls.env["mail.tracking.value"]
|
||||
|
||||
def test_create_tracking_values_html(self):
|
||||
initial_value = "<p>Initial Value</p>"
|
||||
new_value = "<p>New Value</p>"
|
||||
col_name = "comment"
|
||||
col_info = {"type": "html"}
|
||||
record = self.env["res.partner"].create({"name": "Test Partner"})
|
||||
|
||||
values = self.MailTracking._create_tracking_values(
|
||||
initial_value, new_value, col_name, col_info, record
|
||||
)
|
||||
|
||||
self.assertEqual(values["old_value_char"], "Initial Value")
|
||||
self.assertEqual(values["new_value_char"], "New Value")
|
||||
|
||||
def _test_create_tracking_values_property(self, values):
|
||||
property_type_mapped = {
|
||||
"char": "char",
|
||||
"boolean": "integer",
|
||||
"integer": "integer",
|
||||
"float": "float",
|
||||
"date": "datetime",
|
||||
"datetime": "datetime",
|
||||
"selection": "char",
|
||||
"tags": "char",
|
||||
"many2one": "integer",
|
||||
"many2many": "char",
|
||||
}
|
||||
test_properties_info = {
|
||||
"property_01": {"string": "property_01", "type": "char"},
|
||||
"property_02": {"string": "property_02", "type": "boolean"},
|
||||
"property_03": {"string": "property_03", "type": "integer"},
|
||||
"property_04": {"string": "property_04", "type": "float"},
|
||||
"property_05": {"string": "property_05", "type": "date"},
|
||||
"property_06": {"string": "property_06", "type": "datetime"},
|
||||
"property_07": {
|
||||
"string": "property_07",
|
||||
"type": "selection",
|
||||
"selection": [["key1", "value1"], ["key2", "value2"]],
|
||||
},
|
||||
"property_08": {"string": "property_08", "type": "tags"},
|
||||
"property_09": {
|
||||
"string": "property_09",
|
||||
"type": "many2one",
|
||||
"comodel": self.partner._name,
|
||||
},
|
||||
"property_10": {
|
||||
"string": "property_10",
|
||||
"type": "many2many",
|
||||
"comodel": self.partner._name,
|
||||
},
|
||||
}
|
||||
for p_name, col_info in test_properties_info.items():
|
||||
initial_value = values[p_name][0]
|
||||
new_value = values[p_name][1]
|
||||
res = self.MailTracking._create_tracking_values_property(
|
||||
initial_value, new_value, "title", col_info, self.partner
|
||||
)
|
||||
del res["field_info"]
|
||||
f_name = property_type_mapped[col_info["type"]]
|
||||
expected_old_value = initial_value
|
||||
expected_new_value = new_value
|
||||
if col_info["type"] == "date":
|
||||
expected_old_value = (
|
||||
f"{expected_old_value} 00:00:00" if expected_old_value else False
|
||||
)
|
||||
expected_new_value = (
|
||||
f"{expected_new_value} 00:00:00" if expected_new_value else False
|
||||
)
|
||||
elif col_info["type"] == "selection":
|
||||
expected_old_value = values[p_name][2]
|
||||
expected_new_value = values[p_name][3]
|
||||
elif col_info["type"] == "tags":
|
||||
expected_old_value = (
|
||||
", ".join(value for value in expected_old_value)
|
||||
if expected_old_value
|
||||
else ""
|
||||
)
|
||||
expected_new_value = (
|
||||
", ".join(value for value in expected_new_value)
|
||||
if expected_new_value
|
||||
else ""
|
||||
)
|
||||
elif col_info["type"] == "many2one":
|
||||
del res["old_value_char"]
|
||||
del res["new_value_char"]
|
||||
elif col_info["type"] == "many2many":
|
||||
comodel = self.env[col_info["comodel"]]
|
||||
expected_old_value = (
|
||||
comodel.browse(expected_old_value) if expected_old_value else False
|
||||
)
|
||||
expected_new_value = (
|
||||
comodel.browse(expected_new_value) if expected_new_value else False
|
||||
)
|
||||
expected_old_value = (
|
||||
", ".join(expected_old_value.mapped("display_name"))
|
||||
if expected_old_value
|
||||
else ""
|
||||
)
|
||||
expected_new_value = (
|
||||
", ".join(expected_new_value.mapped("display_name"))
|
||||
if expected_new_value
|
||||
else ""
|
||||
)
|
||||
expected_values = {
|
||||
f"old_value_{f_name}": expected_old_value,
|
||||
f"new_value_{f_name}": expected_new_value,
|
||||
}
|
||||
self.assertEqual(res, expected_values)
|
||||
|
||||
def test_mail_tracking_value_properties(self):
|
||||
partner_extra = self.env["res.partner"].create({"name": "Test partner extra"})
|
||||
test_properties_01 = {
|
||||
# property: initial_value, new_value
|
||||
"property_01": ("", "value1"),
|
||||
"property_02": (False, True),
|
||||
"property_03": (0, 10),
|
||||
"property_04": (0, 10.10),
|
||||
"property_05": (False, "2025-01-01"),
|
||||
"property_06": (False, "2025-01-01 00:00:00"),
|
||||
"property_07": (False, "key1", "", "value1"),
|
||||
"property_08": (False, ["tag1", "tag2"]),
|
||||
"property_09": (False, self.partner.id),
|
||||
"property_10": (False, [self.partner.id, partner_extra.id]),
|
||||
}
|
||||
# Test all the property types using as fake title field because there is no
|
||||
# property field in base to test.
|
||||
# We do not want to create a FakeModel and add the property field in partner
|
||||
# because the partner_property module could have conflicts.
|
||||
# 1- Test the case that all the initial values were empty and now have a value
|
||||
self._test_create_tracking_values_property(test_properties_01)
|
||||
# 2- Test the case that all the initial values had something set and now have
|
||||
# a different value
|
||||
test_properties_02 = {
|
||||
# property: initial_value, new_value
|
||||
"property_01": ("value1", "value2"),
|
||||
"property_02": (True, False),
|
||||
"property_03": (10, 11),
|
||||
"property_04": (10.10, 11.10),
|
||||
"property_05": ("2025-01-01", "2025-01-02"),
|
||||
"property_06": ("2025-01-01 00:00:00", "2025-01-02 00:00:00"),
|
||||
"property_07": ("key1", "key2", "value1", "value2"),
|
||||
"property_08": (
|
||||
["tag1", "tag2"],
|
||||
[
|
||||
"tag1",
|
||||
],
|
||||
),
|
||||
"property_09": (self.partner.id, partner_extra.id),
|
||||
"property_10": (
|
||||
[self.partner.id, partner_extra.id],
|
||||
[
|
||||
self.partner.id,
|
||||
],
|
||||
),
|
||||
}
|
||||
self._test_create_tracking_values_property(test_properties_02)
|
||||
# 3- Test the case that all initial values had something set and now has
|
||||
# no value
|
||||
test_properties_03 = {
|
||||
# property: initial_value, new_value
|
||||
"property_01": ("value2", ""),
|
||||
"property_02": (False, True),
|
||||
"property_03": (11, 0),
|
||||
"property_04": (11.10, 0),
|
||||
"property_05": ("2025-01-02", False),
|
||||
"property_06": ("2025-01-02 00:00:00", False),
|
||||
"property_07": ("key1", False, "value1", ""),
|
||||
"property_08": (["tag1", "tag2"], False),
|
||||
"property_09": (self.partner.id, False),
|
||||
"property_10": ([self.partner.id, partner_extra.id], False),
|
||||
}
|
||||
self._test_create_tracking_values_property(test_properties_03)
|
||||
273
tracking_manager/tests/test_tracking_manager.py
Executable file
273
tracking_manager/tests/test_tracking_manager.py
Executable file
@@ -0,0 +1,273 @@
|
||||
# Copyright 2022 Akretion (https://www.akretion.com).
|
||||
# Copyright 2024 Tecnativa - Víctor Martínez
|
||||
# @author Kévin Roche <kevin.roche@akretion.com>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import Command
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestTrackingManager(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.partner_categ_1, cls.partner_categ_2, cls.partner_categ_3 = cls.env[
|
||||
"res.partner.category"
|
||||
].create(
|
||||
[
|
||||
{"name": "FOO"},
|
||||
{"name": "BAR"},
|
||||
{"name": "TOOH"},
|
||||
]
|
||||
)
|
||||
cls.partner = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Foo",
|
||||
"user_ids": [(Command.CREATE, 0, {"login": "007"})],
|
||||
"category_id": [(Command.SET, 0, [cls.partner_categ_1.id])],
|
||||
}
|
||||
)
|
||||
cls.partner_model = cls.env.ref("base.model_res_partner")
|
||||
cls._active_tracking(["user_ids", "category_id"])
|
||||
cls.flush_tracking()
|
||||
cls.partner.message_ids.unlink()
|
||||
|
||||
@classmethod
|
||||
def _active_tracking(cls, fields_list):
|
||||
cls.partner_model.active_custom_tracking = True
|
||||
for field in cls._get_fields(fields_list):
|
||||
field.custom_tracking = True
|
||||
|
||||
@classmethod
|
||||
def _get_fields(cls, fields_list):
|
||||
return cls.partner_model.field_id.filtered(lambda s: s.name in fields_list)
|
||||
|
||||
def test_not_tracked(self):
|
||||
field = self._get_fields(["mobile"])[0]
|
||||
self.assertFalse(field.native_tracking)
|
||||
self.assertFalse(field.custom_tracking)
|
||||
|
||||
def test_native_tracked(self):
|
||||
field = self._get_fields(["email"])[0]
|
||||
self.assertTrue(field.native_tracking)
|
||||
self.assertTrue(field.custom_tracking)
|
||||
|
||||
def test_update_tracked(self):
|
||||
field = self._get_fields(["mobile"])[0]
|
||||
self.assertFalse(field.native_tracking)
|
||||
self.partner_model.automatic_custom_tracking = True
|
||||
self.partner_model.update_custom_tracking()
|
||||
self.assertTrue(field.custom_tracking)
|
||||
|
||||
@classmethod
|
||||
def flush_tracking(cls):
|
||||
"""Force the creation of tracking values."""
|
||||
cls.env["base"].flush_model()
|
||||
cls.env.cr.precommit.run()
|
||||
|
||||
@property
|
||||
def messages(self):
|
||||
# Force the creation of tracking values
|
||||
self.flush_tracking()
|
||||
return self.partner.message_ids
|
||||
|
||||
def test_m2m_add_line(self):
|
||||
self.partner = self.env["res.partner"].browse(self.partner.id)
|
||||
self.partner.write(
|
||||
{"category_id": [(Command.LINK, self.partner_categ_2.id, 0)]}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
tracking = self.messages.tracking_value_ids[0]
|
||||
self.assertEqual(len(tracking), 1)
|
||||
self.assertEqual(tracking.old_value_char, "FOO")
|
||||
self.assertEqual(tracking.new_value_char, "FOO, BAR")
|
||||
|
||||
def test_m2m_delete_line(self):
|
||||
self.partner.write(
|
||||
{"category_id": [(Command.UNLINK, self.partner_categ_1.id, 0)]}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
tracking = self.messages.tracking_value_ids
|
||||
self.assertEqual(len(tracking), 1)
|
||||
self.assertEqual(tracking.old_value_char, "FOO")
|
||||
self.assertEqual(tracking.new_value_char, "")
|
||||
|
||||
def test_m2m_multi_line(self):
|
||||
self.partner.write(
|
||||
{
|
||||
"category_id": [
|
||||
(
|
||||
Command.SET,
|
||||
0,
|
||||
[
|
||||
self.partner_categ_2.id,
|
||||
self.partner_categ_3.id,
|
||||
],
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
tracking = self.messages.tracking_value_ids
|
||||
self.assertEqual(len(tracking), 1)
|
||||
self.assertEqual(tracking.old_value_char, "FOO")
|
||||
self.assertEqual(tracking.new_value_char, "BAR, TOOH")
|
||||
|
||||
def test_o2m_create_indirectly(self):
|
||||
self.partner.write({"user_ids": [(Command.CREATE, 0, {"login": "1234567890"})]})
|
||||
self.assertEqual(len(self.messages), 2)
|
||||
self.assertEqual(self.messages[0].body.count("New"), 1)
|
||||
|
||||
@mute_logger("odoo.models.unlink")
|
||||
def test_o2m_unlink_indirectly(self):
|
||||
self.partner.write(
|
||||
{"user_ids": [(Command.DELETE, self.partner.user_ids[0].id)]}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertIn("Delete", self.messages.body)
|
||||
|
||||
def test_o2m_write_indirectly(self):
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [
|
||||
(Command.UPDATE, self.partner.user_ids[0].id, {"login": "123"})
|
||||
],
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertIn("Change", self.messages.body)
|
||||
|
||||
def test_o2m_write_indirectly_on_not_tracked_fields(self):
|
||||
# Active custom tracking on res.users and remove tracking on login
|
||||
res_users_model = self.env["ir.model"].search([("model", "=", "res.users")])
|
||||
res_users_model.active_custom_tracking = True
|
||||
login_field = res_users_model.field_id.filtered(lambda x: x.name == "login")
|
||||
login_field.custom_tracking = False
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [
|
||||
(Command.UPDATE, self.partner.user_ids[0].id, {"login": "123"})
|
||||
],
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 0)
|
||||
|
||||
@mute_logger("odoo.models.unlink")
|
||||
def test_o2m_create_and_unlink_indirectly(self):
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [
|
||||
(Command.DELETE, self.partner.user_ids[0].id, 0),
|
||||
(Command.CREATE, 0, {"login": "1234567890"}),
|
||||
]
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("New"), 1)
|
||||
self.assertEqual(self.messages.body.count("Delete"), 1)
|
||||
|
||||
def test_o2m_update_m2m_indirectly(self):
|
||||
self.group_extra = self.env["res.groups"].create({"name": "Test group"})
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [
|
||||
(
|
||||
Command.UPDATE,
|
||||
self.partner.user_ids[0].id,
|
||||
{
|
||||
"groups_id": [
|
||||
(
|
||||
6,
|
||||
0,
|
||||
[
|
||||
self.env.ref("base.group_user").id,
|
||||
self.group_extra.id,
|
||||
],
|
||||
)
|
||||
]
|
||||
},
|
||||
),
|
||||
]
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Changed"), 1)
|
||||
|
||||
def test_o2m_update_m2o_indirectly(self):
|
||||
user = self.partner.user_ids[0]
|
||||
action = self.env["ir.actions.act_window"].create(
|
||||
{"name": "test", "type": "ir.actions.act_window", "res_model": user._name}
|
||||
)
|
||||
self.partner.write(
|
||||
{"user_ids": [(Command.UPDATE, user.id, {"action_id": action.id})]}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Changed"), 1)
|
||||
|
||||
@mute_logger("odoo.models.unlink")
|
||||
def test_o2m_write_and_unlink_indirectly(self):
|
||||
# when editing a o2m in some special case
|
||||
# like the computed field amount_tax of purchase order line
|
||||
# some write can be done on a line before behind deleted
|
||||
# line._compute_amount() is called manually inside see link behind
|
||||
# https://github.com/odoo/odoo/blob/009f35f3d3659792ef18ac510a6ec323708becec/addons/purchase/models/purchase.py#L28 # noqa
|
||||
# So we are in a case that we do some change and them we delete them
|
||||
# in that case we should only have one message of deletation
|
||||
# and no error
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [
|
||||
(Command.UPDATE, self.partner.user_ids[0].id, {"login": "123"})
|
||||
],
|
||||
}
|
||||
)
|
||||
self.partner.write(
|
||||
{
|
||||
"user_ids": [(Command.DELETE, self.partner.user_ids[0].id, 0)],
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Change"), 0)
|
||||
self.assertEqual(self.messages.body.count("Delete"), 1)
|
||||
|
||||
def test_o2m_create_directly(self):
|
||||
# Add custom context to prevent message from mail addon
|
||||
self.env["res.users"].with_context(
|
||||
mail_create_nolog=True, mail_notrack=True
|
||||
).create(
|
||||
{
|
||||
"name": "1234567890",
|
||||
"login": "1234567890",
|
||||
"partner_id": self.partner.id,
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("New"), 1)
|
||||
|
||||
@mute_logger("odoo.models.unlink")
|
||||
def test_o2m_unlink_directly(self):
|
||||
self.partner.user_ids.unlink()
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Delete"), 1)
|
||||
|
||||
def test_o2m_update_directly(self):
|
||||
self.partner.user_ids.write({"login": "0987654321"})
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Change :"), 1)
|
||||
|
||||
@mute_logger("odoo.models.unlink")
|
||||
def test_o2m_write_and_unlink_directly(self):
|
||||
# see explanation of test_o2m_write_and_unlink_indirectly
|
||||
self.partner.user_ids.write({"login": "0987654321"})
|
||||
self.partner.user_ids.unlink()
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
self.assertEqual(self.messages.body.count("Change"), 0)
|
||||
self.assertEqual(self.messages.body.count("Delete"), 1)
|
||||
|
||||
def test_o2m_update_record(self):
|
||||
self.env.ref("base.field_res_partner__child_ids").custom_tracking = True
|
||||
child = self.env["res.partner"].create(
|
||||
{"name": "Test child", "parent_id": self.partner.id}
|
||||
)
|
||||
child.write({"parent_id": False})
|
||||
self.assertEqual(len(self.messages), 1)
|
||||
Reference in New Issue
Block a user