diff --git a/bi_sql_editor/README.rst b/bi_sql_editor/README.rst
index 01f21e17..2eb6b3fe 100644
--- a/bi_sql_editor/README.rst
+++ b/bi_sql_editor/README.rst
@@ -77,7 +77,8 @@ To configure this module, you need to:
* Click on the button 'Create SQL View, Indexes and Models'. (this step could
take a while, if view is materialized)
-* If it's a MATERIALIZED view:
+* If it's a MATERIALIZED view:
+
* a cron task is created to refresh
the view. You can so define the frequency of the refresh.
* the size of view (and the indexes is displayed)
@@ -93,9 +94,9 @@ Usage
To use this module, you need to:
-* Go to 'Reporting' / 'Custom Reports'
+#. Go to 'Reporting' / 'Custom Reports'
-* select the desired report
+#. Select the desired report
.. figure:: /bi_sql_editor/static/description/05_reporting_pivot.png
:width: 800 px
diff --git a/bi_sql_editor/__init__.py b/bi_sql_editor/__init__.py
index cde864ba..e4f76a9e 100644
--- a/bi_sql_editor/__init__.py
+++ b/bi_sql_editor/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
+from .hooks import uninstall_hook
diff --git a/bi_sql_editor/__openerp__.py b/bi_sql_editor/__openerp__.py
index a69a1cad..edd4bf5f 100644
--- a/bi_sql_editor/__openerp__.py
+++ b/bi_sql_editor/__openerp__.py
@@ -21,8 +21,9 @@
'views/menu.xml',
],
'demo': [
- 'demo/res_groups.xml',
- 'demo/bi_sql_view.xml',
+ 'demo/res_groups_demo.xml',
+ 'demo/bi_sql_view_demo.xml',
],
'installable': True,
+ 'uninstall_hook': 'uninstall_hook'
}
diff --git a/bi_sql_editor/demo/bi_sql_view.xml b/bi_sql_editor/demo/bi_sql_view.xml
deleted file mode 100644
index 75231916..00000000
--- a/bi_sql_editor/demo/bi_sql_view.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
- Draft Incorrect SQL View
- incorrect_view
-
-
-
-
-
- Partners View
- partners_view
-
-
-
-
-
-
-
- Modules by Authors
- modules_view
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bi_sql_editor/demo/bi_sql_view_demo.xml b/bi_sql_editor/demo/bi_sql_view_demo.xml
new file mode 100644
index 00000000..7758110b
--- /dev/null
+++ b/bi_sql_editor/demo/bi_sql_view_demo.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+ Draft Incorrect SQL View
+ incorrect_view
+
+
+
+
+
+ Partners View
+ partners_view
+
+
+
+
+
+ Modules by Authors
+ modules_view
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bi_sql_editor/demo/res_groups.xml b/bi_sql_editor/demo/res_groups.xml
deleted file mode 100644
index 2e9eadeb..00000000
--- a/bi_sql_editor/demo/res_groups.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bi_sql_editor/demo/res_groups_demo.xml b/bi_sql_editor/demo/res_groups_demo.xml
new file mode 100644
index 00000000..0a3803c5
--- /dev/null
+++ b/bi_sql_editor/demo/res_groups_demo.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bi_sql_editor/hooks.py b/bi_sql_editor/hooks.py
new file mode 100644
index 00000000..972135e9
--- /dev/null
+++ b/bi_sql_editor/hooks.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright 2015-2017 Onestein ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+from openerp import SUPERUSER_ID
+from openerp.api import Environment
+
+
+def uninstall_hook(cr, registry):
+ env = Environment(cr, SUPERUSER_ID, {})
+ recs = env['bi.sql.view'].search([])
+ for rec in recs:
+ rec.button_set_draft()
diff --git a/bi_sql_editor/models/bi_sql_view.py b/bi_sql_editor/models/bi_sql_view.py
index 685276a5..dffa9b57 100644
--- a/bi_sql_editor/models/bi_sql_view.py
+++ b/bi_sql_editor/models/bi_sql_view.py
@@ -27,14 +27,14 @@ class BiSQLView(models.Model):
_STATE_SQL_EDITOR = [
('model_valid', 'SQL View and Model Created'),
- ('ui_valid', 'Graph, action and Menu Created'),
+ ('ui_valid', 'Views, Action and Menu Created'),
]
technical_name = fields.Char(
string='Technical Name', required=True,
- help="Suffix of the SQL view. (SQL full name will be computed and"
- " prefixed by 'x_bi_sql_view_'. Should have correct"
- "syntax. For more information, see https://www.postgresql.org/"
+ help="Suffix of the SQL view. SQL full name will be computed and"
+ " prefixed by 'x_bi_sql_view_'. Syntax should follow: "
+ "https://www.postgresql.org/"
"docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS")
view_name = fields.Char(
@@ -59,6 +59,14 @@ class BiSQLView(models.Model):
state = fields.Selection(selection_add=_STATE_SQL_EDITOR)
+ view_order = fields.Char(string='View Order',
+ required=True,
+ readonly=False,
+ states={'ui_valid': [('readonly', True)]},
+ default="pivot,graph,tree",
+ help='Comma-separated text. Possible values:'
+ ' "graph", "pivot" or "tree"')
+
query = fields.Text(
help="SQL Request that will be inserted as the view. Take care to :\n"
" * set a name for all your selected fields, specially if you use"
@@ -113,6 +121,16 @@ class BiSQLView(models.Model):
rule_id = fields.Many2one(
string='Odoo Rule', comodel_name='ir.rule', readonly=True)
+ @api.constrains('view_order')
+ @api.multi
+ def _check_view_order(self):
+ for rec in self:
+ if rec.view_order:
+ for vtype in rec.view_order.split(','):
+ if vtype not in ('graph', 'pivot', 'tree'):
+ raise UserError(_(
+ 'Only graph, pivot or tree views are supported'))
+
# Compute Section
@api.depends('is_materialized')
@api.multi
@@ -148,7 +166,7 @@ class BiSQLView(models.Model):
('state', 'not in', ('draft', 'sql_valid'))])
if non_draft_views:
raise UserError(_("You can only unlink draft views"))
- return self.unlink()
+ return super(BiSQLView, self).unlink()
@api.multi
def copy(self, default=None):
@@ -185,7 +203,8 @@ class BiSQLView(models.Model):
for sql_view in self:
if sql_view.state in ('model_valid', 'ui_valid'):
# Drop SQL View (and indexes by cascade)
- sql_view._drop_view()
+ if sql_view.is_materialized:
+ sql_view._drop_view()
# Drop ORM
sql_view._drop_model_and_fields()
@@ -233,7 +252,7 @@ class BiSQLView(models.Model):
'type': 'ir.actions.act_window',
'res_model': self.model_id.model,
'search_view_id': self.search_view_id.id,
- 'view_mode': 'graph,pivot,tree',
+ 'view_mode': self.action_id.view_mode,
}
# Prepare Function
@@ -357,12 +376,20 @@ class BiSQLView(models.Model):
@api.multi
def _prepare_action(self):
self.ensure_one()
+ view_mode = self.view_order
+ first_view = view_mode.split(',')[0]
+ if first_view == 'tree':
+ view_id = self.tree_view_id.id
+ elif first_view == 'pivot':
+ view_id = self.pivot_view_id.id
+ else:
+ view_id = self.graph_view_id.id
return {
'name': self.name,
'res_model': self.model_id.model,
'type': 'ir.actions.act_window',
- 'view_mode': 'graph,pivot,tree',
- 'view_id': self.graph_view_id.id,
+ 'view_mode': view_mode,
+ 'view_id': view_id,
'search_view_id': self.search_view_id.id,
}
@@ -438,7 +465,8 @@ class BiSQLView(models.Model):
@api.multi
def _drop_model_and_fields(self):
for sql_view in self:
- sql_view.model_id.unlink()
+ if sql_view.model_id:
+ sql_view.model_id.unlink()
@api.multi
def _hook_executed_request(self):
@@ -522,16 +550,17 @@ class BiSQLView(models.Model):
@api.multi
def _refresh_materialized_view(self):
for sql_view in self:
- req = "REFRESH %s VIEW %s" % (
- sql_view.materialized_text, sql_view.view_name)
- self._log_execute(req)
- sql_view._refresh_size()
- if sql_view.action_id:
- # Alter name of the action, to display last refresh datetime
- # of the materialized view
- sql_view.action_id.name = "%s (%s)" % (
- self.name,
- datetime.utcnow().strftime(_("%m/%d/%Y %H:%M:%S UTC")))
+ if sql_view.is_materialized:
+ req = "REFRESH %s VIEW %s" % (
+ sql_view.materialized_text, sql_view.view_name)
+ self._log_execute(req)
+ sql_view._refresh_size()
+ if sql_view.action_id:
+ # Alter name of the action, to display last refresh
+ # datetime of the materialized view
+ sql_view.action_id.name = "%s (%s)" % (
+ self.name,
+ datetime.utcnow().strftime(_("%m/%d/%Y %H:%M:%S UTC")))
@api.multi
def _refresh_size(self):
diff --git a/bi_sql_editor/models/bi_sql_view_field.py b/bi_sql_editor/models/bi_sql_view_field.py
index 5c464e13..40ac2436 100644
--- a/bi_sql_editor/models/bi_sql_view_field.py
+++ b/bi_sql_editor/models/bi_sql_view_field.py
@@ -84,7 +84,7 @@ class BiSQLViewField(models.Model):
ttype = fields.Selection(
string='Field Type', selection=_TTYPE_SELECTION, help="Type of the"
- " Odoo field that will be created. Let empty if you don't want to"
+ " Odoo field that will be created. Keep empty if you don't want to"
" create a new field. If empty, this field will not be displayed"
" neither available for search or group by function")
@@ -98,7 +98,7 @@ class BiSQLViewField(models.Model):
many2one_model_id = fields.Many2one(
comodel_name='ir.model', string='Model',
help="For 'Many2one' Odoo field.\n"
- " Co Model of the field.")
+ " Comodel of the field.")
# Compute Section
@api.multi
@@ -174,8 +174,6 @@ class BiSQLViewField(models.Model):
'selection': self.ttype == 'selection' and self.selection or False,
'relation': self.ttype == 'many2one' and
self.many2one_model_id.model or False,
- 'tree_visibility': self.field_description and
- 'available' or 'unavailable',
}
@api.multi
diff --git a/bi_sql_editor/tests/__init__.py b/bi_sql_editor/tests/__init__.py
new file mode 100644
index 00000000..e01cc721
--- /dev/null
+++ b/bi_sql_editor/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+from . import test_bi_sql_view
diff --git a/bi_sql_editor/tests/test_bi_sql_view.py b/bi_sql_editor/tests/test_bi_sql_view.py
new file mode 100644
index 00000000..12af27cd
--- /dev/null
+++ b/bi_sql_editor/tests/test_bi_sql_view.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# Copyright 2017 Onestein ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from openerp.tests.common import TransactionCase, at_install, post_install
+from openerp.exceptions import AccessError
+
+
+class TestBiSqlViewEditor(TransactionCase):
+
+ def setUp(self):
+ super(TestBiSqlViewEditor, self).setUp()
+ self.res_partner = self.env['res.partner']
+ self.res_users = self.env['res.users']
+ self.bi_sql_view = self.env['bi.sql.view']
+ self.group_bi_user = self.env.ref(
+ 'sql_request_abstract.group_sql_request_manager')
+ self.group_user = self.env.ref(
+ 'base.group_user')
+ self.view = self.bi_sql_view.create({
+ 'name': 'Partners View 2',
+ 'is_materialized': False,
+ 'technical_name': 'partners_view_2',
+ 'query': "SELECT name as x_name, street as x_street,"
+ "company_id as x_company_id FROM res_partner "
+ "ORDER BY name"
+ })
+ self.company = self.env.ref('base.main_company')
+ # Create bi user
+ self.bi_user = self._create_user('bi_user', [self.group_bi_user],
+ self.company)
+ self.no_bi_user = self._create_user('no_bi_user', [self.group_user],
+ self.company)
+
+ def _create_user(self, login, groups, company):
+ """Create a user."""
+ group_ids = [group.id for group in groups]
+ user = self.res_users.create({
+ 'name': 'Test BI User',
+ 'login': login,
+ 'password': 'demo',
+ 'email': 'example@yourcompany.com',
+ 'company_id': company.id,
+ 'groups_id': [(6, 0, group_ids)]
+ })
+ return user
+
+ @at_install(False)
+ @post_install(True)
+ def test_process_view(self):
+ view = self.view
+ self.assertEqual(view.state, 'draft', 'state not draft')
+ view.button_validate_sql_expression()
+ self.assertEqual(view.state, 'sql_valid', 'state not sql_valid')
+
+ def test_copy(self):
+ copy_view = self.view.copy()
+ self.assertEqual(
+ copy_view.name, 'Partners View 2 (Copy)', 'Wrong name')
+
+ def test_security(self):
+ with self.assertRaises(AccessError):
+ self.bi_sql_view.sudo(self.no_bi_user.id).search(
+ [('name', '=', 'Partners View 2')])
+ bi = self.bi_sql_view.sudo(self.bi_user.id).search(
+ [('name', '=', 'Partners View 2')])
+ self.assertEqual(len(bi), 1, 'Bi user should not have access to '
+ 'bi %s' % self.view.name)
+
+ def test_unlink(self):
+ self.assertEqual(self.view.state, 'draft', 'state not draft')
+ self.view.button_validate_sql_expression()
+ self.view.unlink()
+ res = self.bi_sql_view.search([('name', '=', 'Partners View 2')])
+ self.assertEqual(len(res), 0, 'View not deleted')
diff --git a/bi_sql_editor/views/view_bi_sql_view.xml b/bi_sql_editor/views/view_bi_sql_view.xml
index 210a90d8..14aecd12 100644
--- a/bi_sql_editor/views/view_bi_sql_view.xml
+++ b/bi_sql_editor/views/view_bi_sql_view.xml
@@ -53,7 +53,8 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-
+
+