diff --git a/server_action_mass_edit/__manifest__.py b/server_action_mass_edit/__manifest__.py
index 0d88db6b41..e2008c1497 100644
--- a/server_action_mass_edit/__manifest__.py
+++ b/server_action_mass_edit/__manifest__.py
@@ -14,6 +14,7 @@
"summary": "Mass Editing",
"depends": [
"base",
+ "onchange_helper",
],
"data": [
"security/ir.model.access.csv",
diff --git a/server_action_mass_edit/models/ir_actions_server.py b/server_action_mass_edit/models/ir_actions_server.py
index d577949103..f82d4191c7 100644
--- a/server_action_mass_edit/models/ir_actions_server.py
+++ b/server_action_mass_edit/models/ir_actions_server.py
@@ -19,6 +19,10 @@ class IrActionsServer(models.Model):
string="Apply domain in lines",
compute="_compute_mass_edit_apply_domain_in_lines",
)
+ mass_edit_play_onchanges = fields.Json(
+ string="Play onchanges from lines",
+ compute="_compute_mass_edit_play_onchanges",
+ )
mass_edit_message = fields.Text(
string="Message",
help="If set, this message will be displayed in the wizard.",
@@ -46,6 +50,14 @@ def _compute_mass_edit_apply_domain_in_lines(self):
record.mass_edit_line_ids.mapped("apply_domain")
)
+ @api.depends("mass_edit_line_ids.apply_onchanges")
+ def _compute_mass_edit_play_onchanges(self):
+ for record in self:
+ record.mass_edit_play_onchanges = {
+ line.field_id.name: line.apply_onchanges
+ for line in record.mass_edit_line_ids
+ }
+
def _run_action_mass_edit_multi(self, eval_context=None):
"""Show report label wizard"""
context = dict(self.env.context)
diff --git a/server_action_mass_edit/models/ir_actions_server_mass_edit_line.py b/server_action_mass_edit/models/ir_actions_server_mass_edit_line.py
index b9a2141f29..a240cb93f8 100644
--- a/server_action_mass_edit/models/ir_actions_server_mass_edit_line.py
+++ b/server_action_mass_edit/models/ir_actions_server_mass_edit_line.py
@@ -47,6 +47,7 @@ class IrActionsServerMassEditLine(models.Model):
default=False,
help="Apply default domain related to field",
)
+ apply_onchanges = fields.Boolean(help="Play field onchanges before writing value")
@api.constrains("server_action_id", "field_id")
def _check_field_model(self):
diff --git a/server_action_mass_edit/tests/test_mass_editing.py b/server_action_mass_edit/tests/test_mass_editing.py
index 4d12de1263..0271677fe1 100644
--- a/server_action_mass_edit/tests/test_mass_editing.py
+++ b/server_action_mass_edit/tests/test_mass_editing.py
@@ -4,6 +4,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from ast import literal_eval
+from unittest.mock import patch
from odoo.exceptions import ValidationError
from odoo.tests import Form, common, new_test_user
@@ -405,3 +406,68 @@ def test_onchange_model_id(self):
result,
None,
)
+
+ @patch("odoo.addons.base.models.res_partner.Partner.onchange_email")
+ def test_wizard_field_onchange(self, patched):
+ server_action_partner = self.env["ir.actions.server"].create(
+ {
+ "name": "Mass Edit Partner",
+ "state": "mass_edit",
+ "model_id": self.env.ref("base.model_res_partner").id,
+ }
+ )
+ self.env["ir.actions.server.mass.edit.line"].create(
+ [
+ {
+ "server_action_id": server_action_partner.id,
+ "field_id": self.env.ref("base.field_res_partner__country_id").id,
+ "apply_onchanges": True,
+ },
+ {
+ "server_action_id": server_action_partner.id,
+ "field_id": self.env.ref("base.field_res_partner__email").id,
+ "apply_onchanges": False,
+ },
+ ]
+ )
+ self.assertEqual(
+ server_action_partner.mass_edit_play_onchanges,
+ {
+ "country_id": True,
+ "email": False,
+ },
+ )
+ us_country = self.env.ref("base.us")
+ mx_country = self.env.ref("base.mx")
+ partners = self.env["res.partner"].create(
+ [
+ {
+ "name": "ACME",
+ "country_id": us_country.id,
+ "state_id": self.env.ref("base.state_us_1").id,
+ },
+ {
+ "name": "Example.com",
+ "country_id": us_country.id,
+ "state_id": self.env.ref("base.state_us_2").id,
+ },
+ ]
+ )
+ self.MassEditingWizard.with_context(
+ server_action_id=server_action_partner.id,
+ active_ids=partners.ids,
+ ).create(
+ {
+ "selection__country_id": "set",
+ "selection__email": "set",
+ "country_id": mx_country,
+ "email": "dummy@email.com",
+ }
+ )
+ for partner in partners:
+ self.assertEqual(partner.country_id, mx_country)
+ # state_id is set to False by _onchange_country_id
+ self.assertFalse(partner.state_id)
+ self.assertEqual(partner.email, "dummy@email.com")
+ # onchange_email is not called
+ patched.assert_not_called()
diff --git a/server_action_mass_edit/views/ir_actions_server.xml b/server_action_mass_edit/views/ir_actions_server.xml
index 869670f000..2bc814cbb6 100644
--- a/server_action_mass_edit/views/ir_actions_server.xml
+++ b/server_action_mass_edit/views/ir_actions_server.xml
@@ -22,6 +22,7 @@
/>
+
diff --git a/server_action_mass_edit/wizard/mass_editing_wizard.py b/server_action_mass_edit/wizard/mass_editing_wizard.py
index bc0f688ab4..5078ab4a39 100644
--- a/server_action_mass_edit/wizard/mass_editing_wizard.py
+++ b/server_action_mass_edit/wizard/mass_editing_wizard.py
@@ -24,6 +24,7 @@ class MassEditingWizard(models.TransientModel):
operation_description_warning = fields.Text(readonly=True)
operation_description_danger = fields.Text(readonly=True)
message = fields.Text(readonly=True)
+ play_onchanges = fields.Json(readonly=True)
@api.model
def default_get(self, fields, active_ids=None):
@@ -67,6 +68,7 @@ def default_get(self, fields, active_ids=None):
"operation_description_warning": operation_description_warning,
"operation_description_danger": operation_description_danger,
"message": server_action.mass_edit_message,
+ "play_onchanges": server_action.mass_edit_play_onchanges,
}
)
server_action_id = self.env.context.get("server_action_id")
@@ -249,42 +251,71 @@ def create(self, vals_list):
active_ids = self.env.context.get("active_ids", [])
if server_action and active_ids:
for vals in vals_list:
- values = {}
- for key, val in vals.items():
- if key.startswith("selection_"):
- split_key = key.split("__", 1)[1]
- if val == "set" or val == "add_o2m":
- values.update({split_key: vals.get(split_key, False)})
-
- elif val == "set_o2m":
- values.update(
- {split_key: [(6, 0, [])] + vals.get(split_key, [])}
+ values = self._prepare_write_values(vals)
+ if values:
+ model = self.env[server_action.model_id.model].with_context(
+ mass_edit=True
+ )
+ records = model.browse(active_ids)
+ # Check if a field in values is set to play onchanges, in which case
+ # each record is to be updated sequentially
+ onchanges_to_play = [
+ fname
+ for fname, val in server_action.mass_edit_play_onchanges.items()
+ if val
+ ]
+ if onchanges_to_play:
+ onchange_values = {
+ k: v for k, v in values.items() if k in onchanges_to_play
+ }
+ not_onchange_values = {
+ k: v
+ for k, v in values.items()
+ if k not in onchanges_to_play
+ }
+ for rec in records:
+ rec_values = onchange_values.copy()
+ rec_values = rec.play_onchanges(
+ rec_values, list(rec_values.keys())
)
+ rec_values.update(not_onchange_values)
+ rec.write(rec_values)
+ else:
+ # If there is not any onchange to play we can write
+ # all the records at once
+ records.write(values)
+ return super().create([{}])
- elif val == "remove":
- values.update({split_key: False})
+ def _prepare_write_values(self, vals):
+ values = {}
+ for key, val in vals.items():
+ if key.startswith("selection_"):
+ split_key = key.split("__", 1)[1]
+ if val == "set" or val == "add_o2m":
+ values.update({split_key: vals.get(split_key, False)})
- elif val == "remove_m2m":
- m2m_list = []
- if vals.get(split_key):
- for m2m_id in vals.get(split_key)[0][2]:
- m2m_list.append((3, m2m_id))
- if m2m_list:
- values.update({split_key: m2m_list})
- else:
- values.update({split_key: [(5, 0, [])]})
+ elif val == "set_o2m":
+ values.update({split_key: [(6, 0, [])] + vals.get(split_key, [])})
- elif val == "add":
- m2m_list = []
- for m2m_id in vals.get(split_key, False)[0][2]:
- m2m_list.append((4, m2m_id))
- values.update({split_key: m2m_list})
+ elif val == "remove":
+ values.update({split_key: False})
- if values:
- self.env[server_action.model_id.model].browse(
- active_ids
- ).with_context(mass_edit=True,).write(values)
- return super().create([{}])
+ elif val == "remove_m2m":
+ m2m_list = []
+ if vals.get(split_key):
+ for m2m_id in vals.get(split_key)[0][2]:
+ m2m_list.append((3, m2m_id))
+ if m2m_list:
+ values.update({split_key: m2m_list})
+ else:
+ values.update({split_key: [(5, 0, [])]})
+
+ elif val == "add":
+ m2m_list = []
+ for m2m_id in vals.get(split_key, False)[0][2]:
+ m2m_list.append((4, m2m_id))
+ values.update({split_key: m2m_list})
+ return values
def _prepare_create_values(self, vals_list):
return vals_list