diff --git a/bi_view_editor/models/bve_view.py b/bi_view_editor/models/bve_view.py
index 3039da31..58ac76a8 100644
--- a/bi_view_editor/models/bve_view.py
+++ b/bi_view_editor/models/bve_view.py
@@ -98,6 +98,19 @@ class BveView(models.Model):
compute='_compute_users',
store=True)
query = fields.Text(compute='_compute_sql_query')
+ over_condition = fields.Text(
+ states={
+ 'draft': [
+ ('readonly', False),
+ ],
+ },
+ readonly=True,
+ help="Condition to be inserted in the OVER part "
+ "of the ID's row_number function.\n"
+ "For instance, 'ORDER BY t1.id' would create "
+ "IDs ordered in the same way as t1's IDs; otherwise "
+ "IDs are assigned with no specific order.",
+ )
er_diagram_image = fields.Binary(compute='_compute_er_diagram_image')
_sql_constraints = [
@@ -282,14 +295,23 @@ class BveView(models.Model):
self._cr.execute('DROP TABLE IF EXISTS %s', (AsIs(view_name), ))
# create postgres view
- self.env.cr.execute('CREATE or REPLACE VIEW %s as (%s)', (
- AsIs(view_name), AsIs(query), ))
+ try:
+ with self.env.cr.savepoint():
+ self.env.cr.execute('CREATE or REPLACE VIEW %s as (%s)', (
+ AsIs(view_name), AsIs(query), ))
+ except Exception as e:
+ raise UserError(
+ _("Error creating the view '{query}':\n{error}")
+ .format(
+ query=query,
+ error=e))
- @api.depends('line_ids', 'state')
+ @api.depends('line_ids', 'state', 'over_condition')
def _compute_sql_query(self):
for bve_view in self:
tables_map = {}
- select_str = '\n CAST(row_number() OVER () as integer) AS id'
+ select_str = '\n CAST(row_number() OVER ({}) as integer) AS id' \
+ .format(bve_view.over_condition or '')
for line in bve_view.field_ids:
table = line.table_alias
select = line.field_id.name
diff --git a/bi_view_editor/readme/USAGE.rst b/bi_view_editor/readme/USAGE.rst
index 986ce305..b243f0ef 100644
--- a/bi_view_editor/readme/USAGE.rst
+++ b/bi_view_editor/readme/USAGE.rst
@@ -21,3 +21,6 @@ To access the created BI View with a dedicated menu:
A more advanced UI is also available under the "Details" tab. It provides extra
possibilities for more advanced users, like to use LEFT JOIN instead of the
default INNER JOIN.
+
+It also possible to improve the IDs generation for new views by adding an `Over Condition` in the "SQL" tab, see https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS for further details.
+For instance, an ORDER BY clause helps preventing unreliable behavior when filtering the generated views.
diff --git a/bi_view_editor/tests/test_bi_view.py b/bi_view_editor/tests/test_bi_view.py
index 38e303b8..7404dcd9 100644
--- a/bi_view_editor/tests/test_bi_view.py
+++ b/bi_view_editor/tests/test_bi_view.py
@@ -5,6 +5,7 @@ import json
import odoo
from odoo.tests.common import TransactionCase
+from odoo.tools import mute_logger
from odoo.exceptions import UserError, ValidationError
from ..hooks import post_load, uninstall_hook
@@ -407,3 +408,24 @@ class TestBiViewEditor(TransactionCase):
bi_view1 = self.env['bve.view'].create(vals)
bi_view1.action_create()
self.assertEqual(len(bi_view1.line_ids), 4)
+
+ @mute_logger('odoo.sql_db')
+ def test_20_broken_view(self):
+ """
+ Create a broken query, a nice UserError should be raised.
+ odoo.sql_db logger is muted to avoid the
+ ERROR: bad_query line in the logs.
+ """
+ vals = self.bi_view1_vals
+ vals.update({
+ 'name': 'Test View broken',
+ 'over_condition': 'bad SQL code',
+ })
+ bi_view = self.env['bve.view'].create(vals)
+ with self.assertRaises(UserError) as ue:
+ bi_view.action_create()
+
+ self.assertEqual(bi_view.state, 'draft')
+ self.assertIn(bi_view.over_condition, str(ue.exception))
+ # remove view
+ bi_view.unlink()
diff --git a/bi_view_editor/views/bve_view.xml b/bi_view_editor/views/bve_view.xml
index 2599fc67..101fa0e9 100644
--- a/bi_view_editor/views/bve_view.xml
+++ b/bi_view_editor/views/bve_view.xml
@@ -85,8 +85,9 @@
+
-
+