Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions server_environment_data_encryption/models/server_env_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ def _inverse_server_env(self, field_name):
encrypted_data_obj._encrypted_store_json(
encrypted_data_name, values, env=env
)
# The env fields are non-stored computed fields with no field
# dependency on the storage. Their cache is therefore not
# updated when we store here: refresh the *sibling* fields from
# the values we just wrote, so that a constraint validating a
# sibling field (e.g. microsoft_outlook reading smtp_encryption
# while saving smtp_authentication) sees the current values.
# The written field is left untouched so later inverses of the
# same write still read its freshly-assigned value.
record._update_cache(
{
name: value
for name, value in values.items()
if (
name in record._fields
and name != field_name
and not record.env.cache.contains(
record, record._fields[name]
)
)
}
)

def action_change_env_data_encrypted_fields(self):
action_id = self.env.context.get("params", {}).get("action")
Expand Down
14 changes: 14 additions & 0 deletions server_environment_data_encryption/tests/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models
from odoo.exceptions import ValidationError

# pylint: disable=consider-merging-classes-inherited

Expand All @@ -13,10 +14,23 @@ def _server_env_fields(self):
base_fields = super()._server_env_fields
partner_fields = {
"city": {},
"street": {},
"street2": {},
}
partner_fields.update(base_fields)
return partner_fields

@api.model
def _server_env_global_section_name(self):
return "partner"

@api.constrains("street")
def _check_street2_when_street(self):
"""A constraint reading a sibling env field (``street2``) while another
env field (``street``) is written. It must see the current value of
``street2``, not a stale one."""
for partner in self.filtered(lambda p: p.street):
if not partner.street2:
raise ValidationError(
self.env._("Street2 is required when Street is set.")
)
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ def test_env_dependent_value(self):
self.assertEqual(partner.with_context(environment="test").city, "test city")
self.assertEqual(partner.with_context(environment="prod").city, "prod city")

def test_constraint_reads_sibling_env_field(self):
"""Writing an env field must not corrupt the cache of sibling env
fields read by a constraint in the same transaction.

Regression test: after a cache clear followed by a re-read (what the
web client does before saving a form), writing ``street`` used to leave
``street2`` stale, so the constraint raised although ``street2`` was set.
"""
from odoo.tests import Form

partner = self.env["res.partner"].create({"name": "Fake name"})
partner.write({"street": "Test street", "street2": "Test street2"})
# Simulate the web client save: clear the cache and re-read the record
# before writing, as odoo.tests.Form does.
with Form(partner) as form:
form.street = "New street"
self.assertEqual(partner.street2, "Test street2")

def test_view_with_env_update(self):
self.maxDiff = None
# common class already set test environment (as default)
Expand Down
Loading