From 339b7362fd3121f555ca1da71535479a4569fd6c Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 5 Dec 2023 20:28:32 +0100 Subject: [PATCH 01/10] [IMP] bi_sql_editor : make installation idempotens --- bi_sql_editor/demo/bi_sql_view_demo.xml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/bi_sql_editor/demo/bi_sql_view_demo.xml b/bi_sql_editor/demo/bi_sql_view_demo.xml index d9cc095b6..fd39c9122 100644 --- a/bi_sql_editor/demo/bi_sql_view_demo.xml +++ b/bi_sql_editor/demo/bi_sql_view_demo.xml @@ -49,19 +49,4 @@ FROM ir_module_module ]]> - - - From 4aa882a492b72c59bce8d4fecc5fdf6a93ebe2a4 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 5 Dec 2023 20:42:42 +0100 Subject: [PATCH 02/10] [REF] bi_sql_editor : simplify module tests --- bi_sql_editor/demo/bi_sql_view_demo.xml | 1 + bi_sql_editor/tests/test_bi_sql_view.py | 85 ++++++++++--------------- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/bi_sql_editor/demo/bi_sql_view_demo.xml b/bi_sql_editor/demo/bi_sql_view_demo.xml index fd39c9122..669cd38e3 100644 --- a/bi_sql_editor/demo/bi_sql_view_demo.xml +++ b/bi_sql_editor/demo/bi_sql_view_demo.xml @@ -20,6 +20,7 @@ ORDER BY unexisting_field Partners View partners_view + Date: Tue, 5 Dec 2023 21:22:52 +0100 Subject: [PATCH 03/10] [REF] bi_sql_editor : Make test independant [FIX] bi_sql_editor : allow to pass default values in copy() function --- bi_sql_editor/models/bi_sql_view.py | 10 +++--- bi_sql_editor/tests/test_bi_sql_view.py | 44 +++++++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/bi_sql_editor/models/bi_sql_view.py b/bi_sql_editor/models/bi_sql_view.py index 53026da22..5899cbb3d 100644 --- a/bi_sql_editor/models/bi_sql_view.py +++ b/bi_sql_editor/models/bi_sql_view.py @@ -269,12 +269,10 @@ class BiSQLView(models.Model): def copy(self, default=None): self.ensure_one() default = dict(default or {}) - default.update( - { - "name": _("%s (Copy)") % self.name, - "technical_name": "%s_copy" % self.technical_name, - } - ) + if "name" not in default: + default["name"] = _("%s (Copy)") % self.name + if "technical_name" not in default: + default["technical_name"] = f"{self.technical_name}_copy" return super().copy(default=default) # Action Section diff --git a/bi_sql_editor/tests/test_bi_sql_view.py b/bi_sql_editor/tests/test_bi_sql_view.py index e7285e4fc..5eeb81a05 100644 --- a/bi_sql_editor/tests/test_bi_sql_view.py +++ b/bi_sql_editor/tests/test_bi_sql_view.py @@ -29,20 +29,21 @@ class TestBiSqlViewEditor(SingleTransactionCase): return cls.demo_user def test_process_view(self): - self.assertEqual(self.view.state, "draft") - self.view.button_validate_sql_expression() - self.assertEqual(self.view.state, "sql_valid") - self.view.button_create_sql_view_and_model() - self.assertEqual(self.view.state, "model_valid") - self.view.button_create_ui() - self.assertEqual(self.view.state, "ui_valid") - self.view.button_update_model_access() - self.assertEqual(self.view.has_group_changed, False) + copy_view = self.view.copy(default={"technical_name": "test_process_view"}) + self.assertEqual(copy_view.state, "draft") + copy_view.button_validate_sql_expression() + self.assertEqual(copy_view.state, "sql_valid") + copy_view.button_create_sql_view_and_model() + self.assertEqual(copy_view.state, "model_valid") + copy_view.button_create_ui() + self.assertEqual(copy_view.state, "ui_valid") + copy_view.button_update_model_access() + self.assertEqual(copy_view.has_group_changed, False) # Check that cron works correctly - self.view.cron_id.method_direct_trigger() + copy_view.cron_id.method_direct_trigger() def test_copy(self): - copy_view = self.view.copy() + copy_view = self.view.copy(default={"technical_name": "test_copy"}) self.assertEqual(copy_view.name, f"{self.view.name} (Copy)") def test_security(self): @@ -58,16 +59,25 @@ class TestBiSqlViewEditor(SingleTransactionCase): ) def test_unlink(self): - view_name = self.view.name - self.assertEqual(self.view.state, "ui_valid") + copy_view = self.view.copy( + default={ + "name": "Test Unlink", + "technical_name": "test_unlink", + } + ) + view_name = copy_view.name + copy_view.button_validate_sql_expression() + copy_view.button_create_sql_view_and_model() + copy_view.button_create_ui() + self.assertEqual(copy_view.state, "ui_valid") with self.assertRaises(UserError): - self.view.unlink() - self.view.button_set_draft() + copy_view.unlink() + copy_view.button_set_draft() self.assertNotEqual( - self.view.cron_id, + copy_view.cron_id, False, "Set to draft materialized view should not unlink cron", ) - self.view.unlink() + copy_view.unlink() res = self.bi_sql_view.search([("name", "=", view_name)]) self.assertEqual(len(res), 0, "View not deleted") From bbe268320abc52cd91395dc898043b5f22133383 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 5 Dec 2023 21:34:29 +0100 Subject: [PATCH 04/10] [FIX] bi_sql_editor : do not allow to create model (and sql views) if related model are not set on many2one fields. It prevents the error 'AttributeError: '_unknown' object has no attribute 'id'' --- bi_sql_editor/models/bi_sql_view.py | 11 ++++++++++- bi_sql_editor/tests/test_bi_sql_view.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bi_sql_editor/models/bi_sql_view.py b/bi_sql_editor/models/bi_sql_view.py index 5899cbb3d..677dfea26 100644 --- a/bi_sql_editor/models/bi_sql_view.py +++ b/bi_sql_editor/models/bi_sql_view.py @@ -8,7 +8,7 @@ from datetime import datetime, timedelta from psycopg2 import ProgrammingError from odoo import SUPERUSER_ID, _, api, fields, models -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.tools import sql, table_columns from odoo.tools.safe_eval import safe_eval @@ -278,6 +278,15 @@ class BiSQLView(models.Model): # Action Section def button_create_sql_view_and_model(self): for sql_view in self.filtered(lambda x: x.state == "sql_valid"): + # Check if many2one fields are correctly + bad_fields = sql_view.bi_sql_view_field_ids.filtered( + lambda x: x.ttype == "many2one" and not x.many2one_model_id.id + ) + if bad_fields: + raise ValidationError( + _("Please set related models on the following fields %s") + % ",".join(bad_fields.mapped("name")) + ) # Create ORM and access sql_view._create_model_and_fields() sql_view._create_model_access() diff --git a/bi_sql_editor/tests/test_bi_sql_view.py b/bi_sql_editor/tests/test_bi_sql_view.py index 5eeb81a05..4bfee18cf 100644 --- a/bi_sql_editor/tests/test_bi_sql_view.py +++ b/bi_sql_editor/tests/test_bi_sql_view.py @@ -1,7 +1,7 @@ # Copyright 2017 Onestein () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.exceptions import AccessError, UserError +from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tests import tagged from odoo.tests.common import SingleTransactionCase @@ -81,3 +81,18 @@ class TestBiSqlViewEditor(SingleTransactionCase): copy_view.unlink() res = self.bi_sql_view.search([("name", "=", view_name)]) self.assertEqual(len(res), 0, "View not deleted") + + def test_many2one_not_found(self): + copy_view = self.view.copy( + default={"technical_name": "test_many2one_not_found"} + ) + + copy_view.query = "SELECT parent_id as x_weird_name_id FROM res_partner;" + copy_view.button_validate_sql_expression() + field_lines = copy_view.bi_sql_view_field_ids + self.assertEqual(len(field_lines), 1) + self.assertEqual(field_lines[0].ttype, "many2one") + self.assertEqual(field_lines[0].many2one_model_id.id, False) + + with self.assertRaises(ValidationError): + copy_view.button_create_sql_view_and_model() From ad0918ed395b27aaeca89cb2c390493de79406e8 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 5 Dec 2023 22:14:49 +0100 Subject: [PATCH 05/10] [FIX] bi_sql_editor : use correct key word 'optional'. (and not 'option') in tree view --- bi_sql_editor/models/bi_sql_view_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bi_sql_editor/models/bi_sql_view_field.py b/bi_sql_editor/models/bi_sql_view_field.py index 2a89e291f..c167bad5a 100644 --- a/bi_sql_editor/models/bi_sql_view_field.py +++ b/bi_sql_editor/models/bi_sql_view_field.py @@ -233,12 +233,12 @@ class BiSQLViewField(models.Model): if self.tree_visibility == "invisible": visibility_text = 'invisible="1"' elif self.tree_visibility == "optional_hide": - visibility_text = 'option="hide"' + visibility_text = 'optional="hide"' elif self.tree_visibility == "optional_show": - visibility_text = 'option="show"' + visibility_text = 'optional="show"' return ( - f"""\n""" ) From de90dedab21aa6a2447c6e60787b5065ae0c951c Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 5 Dec 2023 22:57:12 +0100 Subject: [PATCH 06/10] [IMP] bi_sql_editor : allow to reset to the previous state, and not only into 'draft' state --- bi_sql_editor/models/bi_sql_view.py | 47 +++++++++++++----------- bi_sql_editor/views/view_bi_sql_view.xml | 30 +++++++++------ 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/bi_sql_editor/models/bi_sql_view.py b/bi_sql_editor/models/bi_sql_view.py index 677dfea26..53a2e326f 100644 --- a/bi_sql_editor/models/bi_sql_view.py +++ b/bi_sql_editor/models/bi_sql_view.py @@ -304,28 +304,33 @@ class BiSQLView(models.Model): sql_view.cron_id.active = True sql_view.state = "model_valid" + def button_reset_to_model_valid(self): + views = self.filtered(lambda x: x.state == "ui_valid") + views.mapped("tree_view_id").unlink() + views.mapped("graph_view_id").unlink() + views.mapped("pivot_view_id").unlink() + views.mapped("search_view_id").unlink() + views.mapped("action_id").unlink() + views.mapped("menu_id").unlink() + return views.write({"state": "model_valid"}) + + def button_reset_to_sql_valid(self): + self.button_reset_to_model_valid() + views = self.filtered(lambda x: x.state == "model_valid") + for sql_view in views: + # Drop SQL View (and indexes by cascade) + if sql_view.is_materialized: + sql_view._drop_view() + if sql_view.cron_id: + sql_view.cron_id.active = False + # Drop ORM + sql_view._drop_model_and_fields() + return views.write({"state": "sql_valid"}) + def button_set_draft(self): - for sql_view in self.filtered(lambda x: x.state != "draft"): - sql_view.menu_id.unlink() - sql_view.action_id.unlink() - sql_view.tree_view_id.unlink() - sql_view.graph_view_id.unlink() - sql_view.pivot_view_id.unlink() - sql_view.search_view_id.unlink() - - if sql_view.state in ("model_valid", "ui_valid"): - # Drop SQL View (and indexes by cascade) - if sql_view.is_materialized: - sql_view._drop_view() - - if sql_view.cron_id: - sql_view.cron_id.active = False - - # Drop ORM - sql_view._drop_model_and_fields() - - super(BiSQLView, sql_view).button_set_draft() - return True + self.button_reset_to_model_valid() + self.button_reset_to_sql_valid() + return super().button_set_draft() def button_create_ui(self): self.tree_view_id = self.env["ir.ui.view"].create(self._prepare_tree_view()).id diff --git a/bi_sql_editor/views/view_bi_sql_view.xml b/bi_sql_editor/views/view_bi_sql_view.xml index 207440b4e..955bbaeb0 100644 --- a/bi_sql_editor/views/view_bi_sql_view.xml +++ b/bi_sql_editor/views/view_bi_sql_view.xml @@ -34,14 +34,20 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).