Skip to content

Commit

Permalink
[IMP] server_action_mass_edit: Improve error message when update reco…
Browse files Browse the repository at this point in the history
…rd in batch
  • Loading branch information
carlosdauden committed Oct 28, 2024
1 parent 33f9425 commit 3edac08
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 10 deletions.
2 changes: 2 additions & 0 deletions server_action_mass_edit/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ Contributors

* Jairo Llopis
* Víctor Martínez
* Carlos Dauden
* Tatiana Deribina <[email protected]>
* Hieu, Vo Minh Bao <[email protected]>

Maintainers
~~~~~~~~~~~
Expand Down
22 changes: 21 additions & 1 deletion server_action_mass_edit/demo/mass_editing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,28 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
<field name="field_id" ref="base.field_res_partner_title__shortcut" />
</record>

<!-- Mass Edit Partner -->
<record id="mass_editing_partner" model="ir.actions.server">
<field name="state">mass_edit</field>
<field name="name">Mass Edit</field>
<field name="model_id" ref="base.model_res_partner" />
</record>
<record id="mass_editing_partner_line_1" model="mass.editing.line">
<field name="server_action_id" ref="mass_editing_partner" />
<field name="field_id" ref="base.field_res_partner__parent_id" />
</record>
<record id="mass_editing_partner_line_2" model="mass.editing.line">
<field name="server_action_id" ref="mass_editing_partner" />
<field name="field_id" ref="base.field_res_partner__type" />
</record>
<record id="mass_editing_partner_line_3" model="mass.editing.line">
<field name="server_action_id" ref="mass_editing_partner" />
<field name="field_id" ref="base.field_res_partner__name" />
</record>
<!-- Add context actions -->
<function model="ir.actions.server" name="create_action">
<value eval="[ref('mass_editing_user'), ref('mass_editing_partner_title')]" />
<value
eval="[ref('mass_editing_user'), ref('mass_editing_partner_title'), ref('mass_editing_partner')]"
/>
</function>
</odoo>
2 changes: 2 additions & 0 deletions server_action_mass_edit/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@

* Jairo Llopis
* Víctor Martínez
* Carlos Dauden
* Tatiana Deribina <[email protected]>
* Hieu, Vo Minh Bao <[email protected]>
13 changes: 9 additions & 4 deletions server_action_mass_edit/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

/*
:Author: David Goodger ([email protected])
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.

See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Expand Down Expand Up @@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }

pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
Expand All @@ -300,7 +301,7 @@
span.pre {
white-space: pre }

span.problematic {
span.problematic, pre.problematic {
color: red }

span.section-subtitle {
Expand Down Expand Up @@ -472,15 +473,19 @@ <h2><a class="toc-backref" href="#toc-entry-7">Contributors</a></h2>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a><ul>
<li>Jairo Llopis</li>
<li>Víctor Martínez</li>
<li>Carlos Dauden</li>
</ul>
</li>
<li>Tatiana Deribina &lt;<a class="reference external" href="mailto:tatiana.deribina&#64;spritnit.fi">tatiana.deribina&#64;spritnit.fi</a>&gt;</li>
<li>Hieu, Vo Minh Bao &lt;<a class="reference external" href="mailto:hieu.vmb&#64;komit-consulting.com">hieu.vmb&#64;komit-consulting.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
Expand Down
50 changes: 49 additions & 1 deletion server_action_mass_edit/tests/test_mass_editing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from ast import literal_eval

from odoo.exceptions import ValidationError
from odoo.exceptions import UserError, ValidationError
from odoo.tests import Form, common, new_test_user

from odoo.addons.base.models.ir_actions import IrActionsServer
Expand All @@ -27,12 +27,16 @@ def setUp(self):

self.MassEditingWizard = self.env["mass.editing.wizard"]
self.ResPartnerTitle = self.env["res.partner.title"]
self.ResPartner = self.env["res.partner"]
self.ResLang = self.env["res.lang"]
self.IrActionsActWindow = self.env["ir.actions.act_window"]

self.mass_editing_user = self.env.ref(
"server_action_mass_edit.mass_editing_user"
)
self.mass_editing_partner = self.env.ref(
"server_action_mass_edit.mass_editing_partner"
)
self.mass_editing_partner_title = self.env.ref(
"server_action_mass_edit.mass_editing_partner_title"
)
Expand All @@ -47,6 +51,7 @@ def setUp(self):
groups="base.group_system",
)
self.partner_title = self._create_partner_title()
self.invoice_partner = self._create_invoice_partner()

def _create_partner_title(self):
"""Create a Partner Title."""
Expand All @@ -62,6 +67,14 @@ def _create_partner_title(self):
)
return partner_title

def _create_invoice_partner(self):
invoice_partner = self.ResPartner.create(
{
"type": "invoice",
}
)
return invoice_partner

def _create_wizard_and_apply_values(self, server_action, items, vals):
action = server_action.with_context(
active_model=items._name,
Expand Down Expand Up @@ -405,3 +418,38 @@ def test_onchange_model_id(self):
result,
None,
)

def test_mass_edit_partner_user_error(self):
vals = {
"selection__parent_id": "set",
"parent_id": self.invoice_partner.id,
"write_record_by_record": True,
}
action = self.mass_editing_partner.with_context(
active_model=self.invoice_partner._name,
active_ids=self.invoice_partner.ids,
).run()
try:
self.env[action["res_model"]].with_context(
**literal_eval(action["context"]),
).create(vals)
except Exception as e:
self.assertEqual(type(e), UserError)

def test_mass_edit_partner_sql_error(self):
vals = {
"selection__type": "set",
"type": "contact",
"write_record_by_record": True,
"selection__name": "remove",
}
action = self.mass_editing_partner.with_context(
active_model=self.invoice_partner._name,
active_ids=self.invoice_partner.ids,
).run()
try:
self.env[action["res_model"]].with_context(
**literal_eval(action["context"]),
).create(vals)
except Exception as e:
self.assertEqual(type(e), UserError)
61 changes: 57 additions & 4 deletions server_action_mass_edit/wizard/mass_editing_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import json
import logging

from lxml import etree
from psycopg2 import IntegrityError

from odoo import _, api, fields, models
from odoo.exceptions import (
AccessDenied,
AccessError,
MissingError,
UserError,
ValidationError,
)

from odoo.addons.base.models.ir_ui_view import (
transfer_modifiers_to_node,
Expand All @@ -24,6 +33,12 @@ class MassEditingWizard(models.TransientModel):
operation_description_warning = fields.Text(readonly=True)
operation_description_danger = fields.Text(readonly=True)
message = fields.Text(readonly=True)
write_record_by_record = fields.Boolean(
help="This option will write the records one by one, instead of all at once.\n"
"This is useful when you are editing a lot of records and one of the "
"records raises an error. \n With this option, the error message will be "
"more specific to facillitate the undertanding of the error."
)

@api.model
def default_get(self, fields, active_ids=None):
Expand Down Expand Up @@ -243,12 +258,15 @@ def _clean_check_company_field_domain(self, TargetModel, field, field_info):
return field_info

@api.model_create_multi
def create(self, vals_list):
def create(self, vals_list): # noqa: C901
server_action_id = self.env.context.get("server_action_id")
server_action = self.env["ir.actions.server"].sudo().browse(server_action_id)
active_ids = self.env.context.get("active_ids", [])
if server_action and active_ids:
TargetModel = self.env[server_action.model_id.model]
for vals in vals_list:
write_record_by_record = vals.pop("write_record_by_record", False)
logging.warning("write_record_by_record: %s", write_record_by_record)
values = {}
for key, val in vals.items():
if key.startswith("selection_"):
Expand Down Expand Up @@ -281,9 +299,44 @@ def create(self, vals_list):
values.update({split_key: m2m_list})

if values:
self.env[server_action.model_id.model].browse(
active_ids
).with_context(mass_edit=True,).write(values)
target_records = TargetModel.browse(active_ids)
if write_record_by_record:
for target_record in target_records:
try:
target_record.with_context(mass_edit=True).write(values)
except (
AccessDenied,
AccessError,
MissingError,
UserError,
ValidationError,
IntegrityError,
) as oe:
if isinstance(oe, IntegrityError):
sql_error_msg_dict = (
models.convert_pgerror_constraint(
self.env[TargetModel._name],
False,
False,
oe,
)
)
sql_error_message = sql_error_msg_dict.get(
"message", ""
)
oe = Exception(sql_error_message)
raise UserError(
_(
'Failed to process the %(model_name)s "%(name)s" '
"[id: %(id)s]:\n\n%(ue)s",
model_name=server_action.model_id.name,
name=target_record.display_name,
id=target_record.id,
ue=str(oe),
)
) from oe
else:
target_records.with_context(mass_edit=True).write(values)
return super().create([{}])

def _prepare_create_values(self, vals_list):
Expand Down
2 changes: 2 additions & 0 deletions server_action_mass_edit/wizard/mass_editing_wizard.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
type="object"
class="oe_highlight"
/>
<label for="write_record_by_record" />
<field name="write_record_by_record" />
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
Expand Down

0 comments on commit 3edac08

Please sign in to comment.