Browse Source

Drop bi views when uninstalling module

pull/293/head
Andrea Stirpe 6 years ago
committed by Andrea
parent
commit
205433abb1
  1. 2
      bi_view_editor/__init__.py
  2. 3
      bi_view_editor/__manifest__.py
  3. 59
      bi_view_editor/hooks.py
  4. 4
      bi_view_editor/models/ir_model.py
  5. 120
      bi_view_editor/models/models.py
  6. 88
      bi_view_editor/static/src/js/bi_view_editor.FieldList.js
  7. 8
      bi_view_editor/static/src/js/bi_view_editor.JoinNodeDialog.js
  8. 46
      bi_view_editor/static/src/js/bi_view_editor.ModelList.js
  9. 30
      bi_view_editor/static/src/js/bi_view_editor.js
  10. 6
      bi_view_editor/tests/test_bi_view.py
  11. 8
      bi_view_editor/wizard/wizard_ir_model_menu_create.py

2
bi_view_editor/__init__.py

@ -2,4 +2,4 @@
from . import models from . import models
from . import wizard from . import wizard
from .hooks import uninstall_hook
from .hooks import post_load, uninstall_hook

3
bi_view_editor/__manifest__.py

@ -24,6 +24,7 @@
'qweb': [ 'qweb': [
'static/src/xml/bi_view_editor.xml' 'static/src/xml/bi_view_editor.xml'
], ],
'post_load': 'post_load',
'uninstall_hook': 'uninstall_hook',
'installable': True, 'installable': True,
'uninstall_hook': 'uninstall_hook'
} }

59
bi_view_editor/hooks.py

@ -1,6 +1,56 @@
# Copyright 2015-2018 Onestein (<http://www.onestein.eu>) # Copyright 2015-2018 Onestein (<http://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
from odoo import SUPERUSER_ID
from odoo import api, modules
from odoo.tools import existing_tables, topological_sort
_logger = logging.getLogger(__name__)
def _bi_view(_name):
return _name[0:6] == 'x_bve.'
def post_load():
def check_tables_exist(self, cr):
"""
Verify that all tables are present and try to initialize
those that are missing.
"""
# This monkey patch is meant to avoid that the _logger writes
# warning and error messages, while running an update all,
# in case the model is a bi-view-generated model.
env = api.Environment(cr, SUPERUSER_ID, {})
table2model = {
model._table: name for name, model in env.items()
if not model._abstract and not _bi_view(name) # here is the patch
}
missing_tables = set(table2model).difference(
existing_tables(cr, table2model))
if missing_tables:
missing = {table2model[table] for table in missing_tables}
_logger.warning("Models have no table: %s.", ", ".join(missing))
# recreate missing tables following model dependencies
deps = {name: model._depends for name, model in env.items()}
for name in topological_sort(deps):
if name in missing:
_logger.info("Recreate table of model %s.", name)
env[name].init()
# check again, and log errors if tables are still missing
missing_tables = set(table2model).difference(
existing_tables(cr, table2model))
for table in missing_tables:
_logger.error("Model %s has no table.", table2model[table])
modules.registry.Registry.check_tables_exist = check_tables_exist
def uninstall_hook(cr, registry): def uninstall_hook(cr, registry):
# delete dirty data that could cause problems # delete dirty data that could cause problems
@ -11,3 +61,12 @@ def uninstall_hook(cr, registry):
cr.execute(""" cr.execute("""
delete from bve_view where model_name like 'x_bve.%' delete from bve_view where model_name like 'x_bve.%'
""") """)
cr.execute("""
SELECT 'DROP VIEW ' || table_name
FROM information_schema.views
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name like 'x_bve_%'
""")
results = list(cr.fetchall())
for result in results:
cr.execute(result[0])

4
bi_view_editor/models/ir_model.py

@ -270,7 +270,7 @@ class IrModel(models.Model):
@api.model @api.model
def create(self, vals): def create(self, vals):
if self._context and self._context.get('bve'):
if self.env.context and self.env.context.get('bve'):
vals['state'] = 'base' vals['state'] = 'base'
res = super(IrModel, self).create(vals) res = super(IrModel, self).create(vals)
@ -281,7 +281,7 @@ class IrModel(models.Model):
self.env.cr.execute(q, (res.id, )) self.env.cr.execute(q, (res.id, ))
# # update registry # # update registry
if self._context.get('bve'):
if self.env.context.get('bve'):
# setup models; this reloads custom models in registry # setup models; this reloads custom models in registry
self.pool.setup_models(self._cr) self.pool.setup_models(self._cr)

120
bi_view_editor/models/models.py

@ -3,12 +3,9 @@
import logging import logging
from odoo import SUPERUSER_ID
from odoo import _, api, models, modules, tools
from odoo import _, api, models
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools import (existing_tables, topological_sort)
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -17,122 +14,22 @@ def _bi_view(_name):
return _name[0:6] == 'x_bve.' return _name[0:6] == 'x_bve.'
def check_tables_exist(self, cr):
"""
Verify that all tables are present and try to initialize
those that are missing.
"""
# This monkey patch is meant to avoid that the _logger writes
# warning and error messages, while running an update all,
# in case the model is a bi-view-generated model.
env = api.Environment(cr, SUPERUSER_ID, {})
table2model = {
model._table: name for name, model in env.items()
if not model._abstract and not _bi_view(name) # here is the patch
}
missing_tables = set(table2model).difference(
existing_tables(cr, table2model))
if missing_tables:
missing = {table2model[table] for table in missing_tables}
_logger.warning("Models have no table: %s.", ", ".join(missing))
# recreate missing tables following model dependencies
deps = {name: model._depends for name, model in env.items()}
for name in topological_sort(deps):
if name in missing:
_logger.info("Recreate table of model %s.", name)
env[name].init()
# check again, and log errors if tables are still missing
missing_tables = set(table2model).difference(
existing_tables(cr, table2model))
for table in missing_tables:
_logger.error("Model %s has no table.", table2model[table])
modules.registry.Registry.check_tables_exist = check_tables_exist
_auto_init_orig = models.BaseModel._auto_init
@api.model_cr_context @api.model_cr_context
def _auto_init(self): def _auto_init(self):
""" Initialize the database schema of ``self``:
- create the corresponding table,
- create/update the necessary columns/tables for fields,
- initialize new columns on existing rows,
- add the SQL constraints given on the model,
- add the indexes on indexed fields,
Also prepare post-init stuff to:
- add foreign key constraints,
- reflect models, fields, relations and constraints,
- mark fields to recompute on existing records.
Note: you should not override this method. Instead, you can modify
the model's database schema by overriding method :meth:`~.init`,
which is called right after this one.
"""
# This monkey patch is meant to fix an error (probably # This monkey patch is meant to fix an error (probably
# introduced by https://github.com/odoo/odoo/pull/15412), while # introduced by https://github.com/odoo/odoo/pull/15412), while
# running an update all. The _auto_init() method invoked during # running an update all. The _auto_init() method invoked during
# an update all is the one of BaseModel, and not the one of Base. # an update all is the one of BaseModel, and not the one of Base.
# START OF patch
# This monkey patch seems not working if defined inside the post_load()
if _bi_view(self._name): if _bi_view(self._name):
return return
# END of patch
models.raise_on_invalid_object_name(self._name)
# This prevents anything called by this method (in particular default
# values) from prefetching a field for which the corresponding column
# has not been added in database yet!
self = self.with_context(prefetch_fields=False)
self.pool.post_init(self._reflect)
cr = self._cr
parent_store_compute = False
update_custom_fields = self._context.get('update_custom_fields', False)
must_create_table = not tools.table_exists(cr, self._table)
if self._auto:
if must_create_table:
tools.create_model_table(cr, self._table, self._description)
if self._parent_store:
if not tools.column_exists(cr, self._table, 'parent_left'):
self._create_parent_columns()
parent_store_compute = True
self._check_removed_columns(log=False)
# update the database schema for fields
columns = tools.table_columns(cr, self._table)
def recompute(field):
_logger.info("Storing computed values of %s", field)
recs = self.with_context(active_test=False).search([])
recs._recompute_todo(field)
for field in self._fields.values():
if not field.store:
continue
if field.manual and not update_custom_fields:
continue # don't update custom fields
new = field.update_db(self, columns)
if new and field.compute:
self.pool.post_init(recompute, field)
if self._auto:
self._add_sql_constraints()
if must_create_table:
self._execute_sql()
if parent_store_compute:
self._parent_store_compute()
return _auto_init_orig(self)
models.BaseModel._auto_init = _auto_init models.BaseModel._auto_init = _auto_init
@ -141,11 +38,6 @@ models.BaseModel._auto_init = _auto_init
class Base(models.AbstractModel): class Base(models.AbstractModel):
_inherit = 'base' _inherit = 'base'
@api.model
def _auto_init(self):
if not _bi_view(self._name):
super(Base, self)._auto_init()
@api.model @api.model
def _setup_complete(self): def _setup_complete(self):
if not _bi_view(self._name): if not _bi_view(self._name):

88
bi_view_editor/static/src/js/bi_view_editor.FieldList.js

@ -1,7 +1,7 @@
/* Copyright 2015-2018 Onestein (<http://www.onestein.eu>) /* Copyright 2015-2018 Onestein (<http://www.onestein.eu>)
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('bi_view_editor.FieldList', function(require) {
odoo.define('bi_view_editor.FieldList', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
@ -9,14 +9,14 @@ odoo.define('bi_view_editor.FieldList', function(require) {
var Widget = require('web.Widget'); var Widget = require('web.Widget');
var FieldListContextMenu = Widget.extend({ var FieldListContextMenu = Widget.extend({
start: function() {
start: function () {
var res = this._super.apply(this, arguments); var res = this._super.apply(this, arguments);
this.$el.mouseleave(function() {
this.$el.mouseleave(function () {
$(this).addClass('hidden'); $(this).addClass('hidden');
}); });
return res; return res;
}, },
open: function(x, y) {
open: function (x, y) {
this.$el.css({ this.$el.css({
'left': x + 'px', 'left': x + 'px',
'top': y + 'px' 'top': y + 'px'
@ -28,7 +28,7 @@ odoo.define('bi_view_editor.FieldList', function(require) {
var FieldListFieldContextMenu = FieldListContextMenu.extend({ var FieldListFieldContextMenu = FieldListContextMenu.extend({
template: 'bi_view_editor.FieldList.FieldContextMenu', template: 'bi_view_editor.FieldList.FieldContextMenu',
open: function(x, y, field) {
open: function (x, y, field) {
this.$el.find('.checkbox-column').prop('checked', field.column); this.$el.find('.checkbox-column').prop('checked', field.column);
this.$el.find('.checkbox-row').prop('checked', field.row); this.$el.find('.checkbox-row').prop('checked', field.row);
this.$el.find('.checkbox-measure').prop('checked', field.measure); this.$el.find('.checkbox-measure').prop('checked', field.measure);
@ -47,7 +47,7 @@ odoo.define('bi_view_editor.FieldList', function(require) {
var events = this._super(x, y, field); var events = this._super(x, y, field);
this.$el.find('input').unbind('change'); this.$el.find('input').unbind('change');
this.$el.find('input').change(function() {
this.$el.find('input').change(function () {
var $checkbox = $(this); var $checkbox = $(this);
var property = $checkbox.attr('data-for'); var property = $checkbox.attr('data-for');
field[property] = $checkbox.is(':checked'); field[property] = $checkbox.is(':checked');
@ -60,12 +60,12 @@ odoo.define('bi_view_editor.FieldList', function(require) {
var FieldListJoinContextMenu = FieldListContextMenu.extend({ var FieldListJoinContextMenu = FieldListContextMenu.extend({
template: 'bi_view_editor.FieldList.JoinContextMenu', template: 'bi_view_editor.FieldList.JoinContextMenu',
open: function(x, y, node) {
open: function (x, y, node) {
this.$el.find('.checkbox-join-left').prop('checked', node.join_left); this.$el.find('.checkbox-join-left').prop('checked', node.join_left);
var events = this._super(x, y, node); var events = this._super(x, y, node);
this.$el.find('input').unbind('change'); this.$el.find('input').unbind('change');
this.$el.find('input').change(function() {
this.$el.find('input').change(function () {
var $checkbox = $(this); var $checkbox = $(this);
var property = $checkbox.attr('data-for'); var property = $checkbox.attr('data-for');
node[property] = $checkbox.is(':checked'); node[property] = $checkbox.is(':checked');
@ -81,7 +81,7 @@ odoo.define('bi_view_editor.FieldList', function(require) {
'click .delete-button': 'removeClicked', 'click .delete-button': 'removeClicked',
'keyup input[name="description"]': 'keyupDescription' 'keyup input[name="description"]': 'keyupDescription'
}, },
start: function() {
start: function () {
var res = this._super.apply(this, arguments); var res = this._super.apply(this, arguments);
this.contextmenu = new FieldListFieldContextMenu(this); this.contextmenu = new FieldListFieldContextMenu(this);
this.contextmenu.appendTo(this.$el); this.contextmenu.appendTo(this.$el);
@ -91,8 +91,8 @@ odoo.define('bi_view_editor.FieldList', function(require) {
this.mode = null; this.mode = null;
return res; return res;
}, },
setMode: function(mode) {
if(mode === 'readonly') {
setMode: function (mode) {
if (mode === 'readonly') {
this.$el.find('input[type="text"]').attr('disabled', true); this.$el.find('input[type="text"]').attr('disabled', true);
this.$el.find(".delete-button:last").addClass('hidden'); this.$el.find(".delete-button:last").addClass('hidden');
} else { } else {
@ -101,7 +101,7 @@ odoo.define('bi_view_editor.FieldList', function(require) {
} }
this.mode = mode; this.mode = mode;
}, },
get: function() {
get: function () {
return $.makeArray(this.$el.find("tbody tr").map(function () { return $.makeArray(this.$el.find("tbody tr").map(function () {
var field = $(this).data('field'); var field = $(this).data('field');
field.description = $(this).find('input[name="description"]').val(); field.description = $(this).find('input[name="description"]').val();
@ -127,20 +127,20 @@ odoo.define('bi_view_editor.FieldList', function(require) {
}); });
return model_data; return model_data;
}, },
add: function(field) {
add: function (field) {
var self = this; var self = this;
field.row = typeof field.row === 'undefined' ? false : field.row; field.row = typeof field.row === 'undefined' ? false : field.row;
field.column = typeof field.column === 'undefined' ? false : field.column; field.column = typeof field.column === 'undefined' ? false : field.column;
field.measure = typeof field.measure === 'undefined' ? false : field.measure; field.measure = typeof field.measure === 'undefined' ? false : field.measure;
field.list = typeof field.list === 'undefined' ? true : field.list; field.list = typeof field.list === 'undefined' ? true : field.list;
field._id = typeof field._id === 'undefined' ? _.uniqueId('node_') : field._id; field._id = typeof field._id === 'undefined' ? _.uniqueId('node_') : field._id;
if(field.join_node) {
if (field.join_node) {
field.join_left = typeof field.join_left === 'undefined' ? false : field.join_left; field.join_left = typeof field.join_left === 'undefined' ? false : field.join_left;
} }
var i = 0; var i = 0;
var name = field.name; var name = field.name;
while (this.get().filter(function(item) {
while (this.get().filter(function (item) {
return item.name === field.name; return item.name === field.name;
}).length > 0) { }).length > 0) {
field.name = name + '_' + i; field.name = name + '_' + i;
@ -150,9 +150,9 @@ odoo.define('bi_view_editor.FieldList', function(require) {
// Render table row // Render table row
var $html = $(qweb.render(field.join_node ? 'bi_view_editor.JoinListItem' : 'bi_view_editor.FieldListItem', { var $html = $(qweb.render(field.join_node ? 'bi_view_editor.JoinListItem' : 'bi_view_editor.FieldListItem', {
'field': field 'field': field
})).data('field', field).contextmenu(function(e) {
})).data('field', field).contextmenu(function (e) {
var $item = $(this); var $item = $(this);
if(self.mode === 'readonly') {
if (self.mode === 'readonly') {
return; return;
} }
e.preventDefault(); e.preventDefault();
@ -165,7 +165,7 @@ odoo.define('bi_view_editor.FieldList', function(require) {
this.$el.find(".delete-button:last").removeClass('hidden'); this.$el.find(".delete-button:last").removeClass('hidden');
this.order(); this.order();
}, },
remove: function(id) {
remove: function (id) {
var $item = this.$el.find('tr[data-id="' + id + '"]'); var $item = this.$el.find('tr[data-id="' + id + '"]');
$item.remove(); $item.remove();
this.cleanJoinNodes(); this.cleanJoinNodes();
@ -173,53 +173,53 @@ odoo.define('bi_view_editor.FieldList', function(require) {
this.$el.find(".delete-button:last").removeClass('hidden'); this.$el.find(".delete-button:last").removeClass('hidden');
this.trigger('removed', id); this.trigger('removed', id);
}, },
set: function(fields) {
set: function (fields) {
var set_fields = fields; var set_fields = fields;
if (!set_fields) { if (!set_fields) {
set_fields = []; set_fields = [];
} }
this.$el.find('tbody tr').remove(); this.$el.find('tbody tr').remove();
for(var i = 0; i < set_fields.length; i++) {
for (var i = 0; i < set_fields.length; i++) {
this.add(set_fields[i]); this.add(set_fields[i]);
} }
this.$el.find(".delete-button").addClass('hidden'); this.$el.find(".delete-button").addClass('hidden');
this.$el.find(".delete-button:last").removeClass('hidden'); this.$el.find(".delete-button:last").removeClass('hidden');
}, },
openContextMenu: function($item, x, y) {
openContextMenu: function ($item, x, y) {
var field = $item.data('field'); var field = $item.data('field');
var contextmenu = field.join_node ? this.contextmenu_join : this.contextmenu; var contextmenu = field.join_node ? this.contextmenu_join : this.contextmenu;
// Temporary disable contextmenu for join node (until left join is implemented) // Temporary disable contextmenu for join node (until left join is implemented)
if (field.join_node) { if (field.join_node) {
return; return;
} }
contextmenu.open(x - 20, y - 20, $item.data('field')).on('change', function(f) {
contextmenu.open(x - 20, y - 20, $item.data('field')).on('change', function (f) {
$item.data('field', f); $item.data('field', f);
this.refreshItem($item); this.refreshItem($item);
this.trigger('updated'); this.trigger('updated');
}.bind(this)); }.bind(this));
}, },
refreshItem: function($item) {
refreshItem: function ($item) {
var data = $item.data('field'); var data = $item.data('field');
var $attributes = $item.find('span[data-for], img[data-for]'); var $attributes = $item.find('span[data-for], img[data-for]');
$.each($attributes, function() {
$.each($attributes, function () {
var $attribute = $(this); var $attribute = $(this);
var value = data[$attribute.attr('data-for')]; var value = data[$attribute.attr('data-for')];
if(value) {
if (value) {
$attribute.removeClass('hidden'); $attribute.removeClass('hidden');
} else { } else {
$attribute.addClass('hidden'); $attribute.addClass('hidden');
} }
}); });
}, },
removeClicked: function(e) {
removeClicked: function (e) {
var $button = $(e.currentTarget); var $button = $(e.currentTarget);
var id = $button.attr('data-id'); var id = $button.attr('data-id');
this.remove(id); this.remove(id);
}, },
keyupDescription: function() {
keyupDescription: function () {
this.trigger('updated'); this.trigger('updated');
}, },
cleanJoinNodes: function() {
cleanJoinNodes: function () {
var aliases = $.makeArray(this.$el.find("tbody tr").map(function () { var aliases = $.makeArray(this.$el.find("tbody tr").map(function () {
var data = $(this).data('field'); var data = $(this).data('field');
return data.table_alias.localeCompare(data.join_node) > 0 ? data.join_node : data.table_alias; return data.table_alias.localeCompare(data.join_node) > 0 ? data.join_node : data.table_alias;
@ -238,22 +238,22 @@ odoo.define('bi_view_editor.FieldList', function(require) {
} }
}); });
}, },
getOrder: function() {
getOrder: function () {
var items = this.get(); var items = this.get();
var ordered = items.sort(function(a, b) {
var ordered = items.sort(function (a, b) {
var res = a.table_alias.localeCompare(b.table_alias); var res = a.table_alias.localeCompare(b.table_alias);
if (res === 0) { if (res === 0) {
var both_join_node = a.join_node && b.join_node; var both_join_node = a.join_node && b.join_node;
var both_not_join_node = !a.join_node && !b.join_node; var both_not_join_node = !a.join_node && !b.join_node;
if(both_join_node || both_not_join_node) {
if (both_join_node || both_not_join_node) {
return 0; return 0;
} else if(!a.join_node && b.join_node) {
if(b.table_alias.localeCompare(b.join_node) > 0) {
} else if (!a.join_node && b.join_node) {
if (b.table_alias.localeCompare(b.join_node) > 0) {
return 1; return 1;
} }
return -1; return -1;
} else if(a.join_node && !b.join_node) {
if(a.table_alias.localeCompare(a.join_node) > 0) {
} else if (a.join_node && !b.join_node) {
if (a.table_alias.localeCompare(a.join_node) > 0) {
return -1; return -1;
} }
return 1; return 1;
@ -263,16 +263,16 @@ odoo.define('bi_view_editor.FieldList', function(require) {
}); });
var res = []; var res = [];
_.each(ordered, function(item) {
var already_exists = _.findIndex(res, function(f) {
_.each(ordered, function (item) {
var already_exists = _.findIndex(res, function (f) {
return f._id === item._id; return f._id === item._id;
}) !== -1; }) !== -1;
if(already_exists) {
if (already_exists) {
return; return;
} }
res.push(item); res.push(item);
if(item.join_node) {
var join_node_fields = _.filter(ordered, function(f) {
if (item.join_node) {
var join_node_fields = _.filter(ordered, function (f) {
return f.table_alias === item.join_node && !f.join_node; return f.table_alias === item.join_node && !f.join_node;
}); });
res = _.union(res, join_node_fields); res = _.union(res, join_node_fields);
@ -280,15 +280,15 @@ odoo.define('bi_view_editor.FieldList', function(require) {
}); });
return res; return res;
}, },
order: function() {
order: function () {
var order = this.getOrder(); var order = this.getOrder();
var $rows = this.$el.find("tbody tr"); var $rows = this.$el.find("tbody tr");
$rows.sort(function(a, b) {
var a_index = _.findIndex(order, function(item) {
$rows.sort(function (a, b) {
var a_index = _.findIndex(order, function (item) {
return item._id === $(a).data('field')._id; return item._id === $(a).data('field')._id;
}); });
var b_index = _.findIndex(order, function(item) {
var b_index = _.findIndex(order, function (item) {
return item._id === $(b).data('field')._id; return item._id === $(b).data('field')._id;
}); });
return a_index - b_index; return a_index - b_index;

8
bi_view_editor/static/src/js/bi_view_editor.JoinNodeDialog.js

@ -14,9 +14,9 @@ odoo.define('bi_view_editor.JoinNodeDialog', function (require) {
'/bi_view_editor/static/src/xml/bi_view_editor.xml' '/bi_view_editor/static/src/xml/bi_view_editor.xml'
]), ]),
events: { events: {
"click li": "choiceClicked",
"click li": "choiceClicked"
}, },
init: function(parent, options, choices, model_data) {
init: function (parent, options, choices, model_data) {
this.choices = choices; this.choices = choices;
// Prepare data for view // Prepare data for view
for (var i = 0; i < choices.length; i++) { for (var i = 0; i < choices.length; i++) {
@ -36,11 +36,11 @@ odoo.define('bi_view_editor.JoinNodeDialog', function (require) {
text: _t("Cancel"), text: _t("Cancel"),
classes: "btn-default o_form_button_cancel", classes: "btn-default o_form_button_cancel",
close: true close: true
}],
}]
}); });
this._super(parent, defaults); this._super(parent, defaults);
}, },
choiceClicked: function(e) {
choiceClicked: function (e) {
this.trigger('chosen', { this.trigger('chosen', {
choice: this.choices[$(e.currentTarget).attr('data-index')] choice: this.choices[$(e.currentTarget).attr('data-index')]
}); });

46
bi_view_editor/static/src/js/bi_view_editor.ModelList.js

@ -14,7 +14,7 @@ odoo.define('bi_view_editor.ModelList', function (require) {
events: { events: {
'keyup .search-bar > input': 'filterChanged' 'keyup .search-bar > input': 'filterChanged'
}, },
init: function(parent) {
init: function (parent) {
var res = this._super(parent); var res = this._super(parent);
this.active_models = []; this.active_models = [];
this.cache_fields = {}; this.cache_fields = {};
@ -22,8 +22,8 @@ odoo.define('bi_view_editor.ModelList', function (require) {
this.mode = null; this.mode = null;
return res; return res;
}, },
setMode: function(mode) {
if(mode === 'readonly') {
setMode: function (mode) {
if (mode === 'readonly') {
this.$el.find('.search-bar').attr('disabled', true); this.$el.find('.search-bar').attr('disabled', true);
this.$el.find('.class-list, .class').addClass('readonly'); this.$el.find('.class-list, .class').addClass('readonly');
} else { } else {
@ -32,17 +32,17 @@ odoo.define('bi_view_editor.ModelList', function (require) {
} }
this.mode = mode; this.mode = mode;
}, },
isActive: function(id) {
isActive: function (id) {
return this.active_models.indexOf(id) !== -1; return this.active_models.indexOf(id) !== -1;
}, },
removeAsActive: function(id) {
removeAsActive: function (id) {
var i = this.active_models.indexOf(id); var i = this.active_models.indexOf(id);
this.active_models.splice(i, 1); this.active_models.splice(i, 1);
}, },
addAsActive: function(id) {
addAsActive: function (id) {
this.active_models.push(id); this.active_models.push(id);
}, },
loadModels: function(model_ids) {
loadModels: function (model_ids) {
if (model_ids) { if (model_ids) {
return this._rpc({ return this._rpc({
model: 'ir.model', model: 'ir.model',
@ -61,7 +61,7 @@ odoo.define('bi_view_editor.ModelList', function (require) {
} }
}); });
}, },
loadFields: function(model_id) {
loadFields: function (model_id) {
if (!(model_id in this.cache_fields)) { if (!(model_id in this.cache_fields)) {
var deferred = this._rpc({ var deferred = this._rpc({
model: 'ir.model', model: 'ir.model',
@ -79,39 +79,39 @@ odoo.define('bi_view_editor.ModelList', function (require) {
var self = this; var self = this;
this.$el.find(".class-list").html(''); this.$el.find(".class-list").html('');
_.each(models, function(model) {
_.each(models, function (model) {
var $html = $(qweb.render('bi_view_editor.ModelListItem', { var $html = $(qweb.render('bi_view_editor.ModelListItem', {
'id': model.id, 'id': model.id,
'model': model.model, 'model': model.model,
'name': model.name 'name': model.name
})); }));
$html.find('.class').data('model', model).click(function() {
$html.find('.class').data('model', model).click(function () {
self.modelClicked($(this)); self.modelClicked($(this));
}); });
self.$el.find(".class-list").append($html); self.$el.find(".class-list").append($html);
if (self.isActive(model.id)) { if (self.isActive(model.id)) {
self.loadFields(model.id).done(function(fields) {
self.loadFields(model.id).done(function (fields) {
self.populateFields(fields, model.id); self.populateFields(fields, model.id);
}); });
} }
}); });
}, },
populateFields: function(fields, model_id) {
populateFields: function (fields, model_id) {
var self = this; var self = this;
if(!model_id && fields.length === 0) {
if (!model_id && fields.length === 0) {
return; return;
} }
var data_model_id = model_id; var data_model_id = model_id;
if(!data_model_id) {
if (!data_model_id) {
data_model_id = fields[0].model_id; data_model_id = fields[0].model_id;
} }
var $model_item = this.$el.find(".class[data-id='" + data_model_id + "']"); var $model_item = this.$el.find(".class[data-id='" + data_model_id + "']");
_.each(fields, function(field) {
_.each(fields, function (field) {
var $field = $(qweb.render('bi_view_editor.ModelListFieldItem', { var $field = $(qweb.render('bi_view_editor.ModelListFieldItem', {
name: field.name, name: field.name,
description: field.description description: field.description
})).data('field', field).click(function() {
})).data('field', field).click(function () {
self.fieldClicked($(this)); self.fieldClicked($(this));
}).draggable({ }).draggable({
'revert': 'invalid', 'revert': 'invalid',
@ -124,36 +124,36 @@ odoo.define('bi_view_editor.ModelList', function (require) {
}); });
}, },
modelClicked: function($el) {
modelClicked: function ($el) {
if (this.mode === 'readonly') { if (this.mode === 'readonly') {
return; return;
} }
var model = $el.data('model'); var model = $el.data('model');
$el.parent().find('.field').remove(); $el.parent().find('.field').remove();
if(this.isActive(model.id)) {
if (this.isActive(model.id)) {
this.removeAsActive(model.id); this.removeAsActive(model.id);
} else { } else {
this.addAsActive(model.id); this.addAsActive(model.id);
this.loadFields(model.id).done(function(fields) {
this.loadFields(model.id).done(function (fields) {
this.populateFields(fields, model.id); this.populateFields(fields, model.id);
}.bind(this)); }.bind(this));
} }
}, },
fieldClicked: function($el) {
fieldClicked: function ($el) {
if (this.mode === 'readonly') { if (this.mode === 'readonly') {
return; return;
} }
this.trigger('field_clicked', $el.data('field')); this.trigger('field_clicked', $el.data('field'));
}, },
filterChanged: function(e) {
filterChanged: function (e) {
var $input = $(e.target); var $input = $(e.target);
this.filter($input.val()); this.filter($input.val());
}, },
filter: function(value) {
filter: function (value) {
this.active_models = []; this.active_models = [];
this.$el.find('.field').remove(); this.$el.find('.field').remove();
var val = typeof value === 'undefined' ? this.current_filter : value.toLowerCase(); var val = typeof value === 'undefined' ? this.current_filter : value.toLowerCase();
this.$el.find(".class").each(function() {
this.$el.find(".class").each(function () {
var data = $(this).data('model'); var data = $(this).data('model');
if (data.name.toLowerCase().indexOf(val) === -1 && if (data.name.toLowerCase().indexOf(val) === -1 &&
data.model.toLowerCase().indexOf(val) === -1) { data.model.toLowerCase().indexOf(val) === -1) {

30
bi_view_editor/static/src/js/bi_view_editor.js

@ -17,14 +17,14 @@ odoo.define('bi_view_editor', function (require) {
events: { events: {
"click .clear-btn": "clear" "click .clear-btn": "clear"
}, },
start: function() {
start: function () {
var self = this; var self = this;
var res = this._super.apply(this, arguments); var res = this._super.apply(this, arguments);
// Init ModelList // Init ModelList
this.model_list = new ModelList(this); this.model_list = new ModelList(this);
this.model_list.appendTo(this.$(".body > .left")); this.model_list.appendTo(this.$(".body > .left"));
this.model_list.on('field_clicked', this, function(field) {
this.model_list.on('field_clicked', this, function (field) {
self.addField(_.extend({}, field)); self.addField(_.extend({}, field));
}); });
@ -42,7 +42,7 @@ odoo.define('bi_view_editor', function (require) {
} }
}); });
this.on("change:effective_readonly", this, function() {
this.on("change:effective_readonly", this, function () {
this.updateMode(); this.updateMode();
}); });
this.renderValue(); this.renderValue();
@ -50,22 +50,22 @@ odoo.define('bi_view_editor', function (require) {
this.updateMode(); this.updateMode();
return res; return res;
}, },
clear: function() {
clear: function () {
if (this.mode !== 'readonly') { if (this.mode !== 'readonly') {
this.field_list.set([]); this.field_list.set([]);
this.loadAndPopulateModelList(); this.loadAndPopulateModelList();
this._setValue(this.field_list.get()); this._setValue(this.field_list.get());
} }
}, },
fieldListChanged: function() {
fieldListChanged: function () {
this._setValue(this.field_list.get()); this._setValue(this.field_list.get());
}, },
fieldListRemoved: function() {
fieldListRemoved: function () {
console.log(this.field_list.get()); console.log(this.field_list.get());
this.loadAndPopulateModelList(); this.loadAndPopulateModelList();
this._setValue(this.field_list.get()); this._setValue(this.field_list.get());
}, },
renderValue: function() {
renderValue: function () {
this.field_list.set(JSON.parse(this.value)); this.field_list.set(JSON.parse(this.value));
}, },
updateMode: function () { updateMode: function () {
@ -79,16 +79,16 @@ odoo.define('bi_view_editor', function (require) {
this.field_list.setMode(this.mode); this.field_list.setMode(this.mode);
this.model_list.setMode(this.mode); this.model_list.setMode(this.mode);
}, },
loadAndPopulateModelList: function() {
loadAndPopulateModelList: function () {
var model_ids = null; var model_ids = null;
if (this.field_list.get().length > 0) { if (this.field_list.get().length > 0) {
model_ids = this.field_list.getModelIds(); model_ids = this.field_list.getModelIds();
} }
this.model_list.loadModels(model_ids).done(function(models) {
this.model_list.loadModels(model_ids).done(function (models) {
this.model_list.populateModels(models); this.model_list.populateModels(models);
}.bind(this)); }.bind(this));
}, },
getTableAlias: function(field) {
getTableAlias: function (field) {
if (typeof field.table_alias === 'undefined') { if (typeof field.table_alias === 'undefined') {
var model_ids = this.field_list.getModelIds(); var model_ids = this.field_list.getModelIds();
var n = 0; var n = 0;
@ -99,7 +99,7 @@ odoo.define('bi_view_editor', function (require) {
} }
return field.table_alias; return field.table_alias;
}, },
addFieldAndJoinNode: function(field, join_node) {
addFieldAndJoinNode: function (field, join_node) {
if (join_node.join_node === -1 || join_node.table_alias === -1) { if (join_node.join_node === -1 || join_node.table_alias === -1) {
field.table_alias = this.getTableAlias(field); field.table_alias = this.getTableAlias(field);
if (join_node.join_node === -1) { if (join_node.join_node === -1) {
@ -116,16 +116,16 @@ odoo.define('bi_view_editor', function (require) {
this.loadAndPopulateModelList(); this.loadAndPopulateModelList();
this._setValue(this.field_list.get()); this._setValue(this.field_list.get());
}, },
addField: function(field) {
addField: function (field) {
var data = _.extend({}, field); var data = _.extend({}, field);
var model = new Data.DataSet(this, "ir.model"); var model = new Data.DataSet(this, "ir.model");
var field_data = this.field_list.get(); var field_data = this.field_list.get();
model.call('get_join_nodes', [field_data, data]).then(function(result) {
model.call('get_join_nodes', [field_data, data]).then(function (result) {
if (result.length === 1) { if (result.length === 1) {
this.addFieldAndJoinNode(data, result[0]); this.addFieldAndJoinNode(data, result[0]);
} else if (result.length > 1) { } else if (result.length > 1) {
var dialog = new JoinNodeDialog(this, {}, result, this.field_list.getModelData()); var dialog = new JoinNodeDialog(this, {}, result, this.field_list.getModelData());
dialog.open().on('chosen', this, function(e) {
dialog.open().on('chosen', this, function (e) {
this.addFieldAndJoinNode(data, e.choice); this.addFieldAndJoinNode(data, e.choice);
}); });
} else { } else {
@ -137,7 +137,7 @@ odoo.define('bi_view_editor', function (require) {
} }
}.bind(this)); }.bind(this));
}, },
_parseValue: function(value) {
_parseValue: function (value) {
return JSON.stringify(value); return JSON.stringify(value);
} }
}); });

6
bi_view_editor/tests/test_bi_view.py

@ -239,11 +239,11 @@ class TestBiViewEditor(TransactionCase):
'name': "Test View5", 'name': "Test View5",
'group_ids': [(6, 0, [employees_group.id])], 'group_ids': [(6, 0, [employees_group.id])],
}) })
l = list()
data_list = list()
for r in json.loads(vals['data']): for r in json.loads(vals['data']):
r['model_name'] = "model'name" r['model_name'] = "model'name"
l.append(r)
new_format_data = json.dumps(l)
data_list.append(r)
new_format_data = json.dumps(data_list)
vals.update({'data': new_format_data}) vals.update({'data': new_format_data})
bi_view = self.env['bve.view'].create(vals) bi_view = self.env['bve.view'].create(vals)
self.assertEqual(len(bi_view), 1) self.assertEqual(len(bi_view), 1)

8
bi_view_editor/wizard/wizard_ir_model_menu_create.py

@ -9,9 +9,9 @@ class WizardModelMenuCreate(models.TransientModel):
@api.multi @api.multi
def menu_create(self): def menu_create(self):
if self._context.get('active_model') == 'bve.view':
if self.env.context.get('active_model') == 'bve.view':
self.ensure_one() self.ensure_one()
active_id = self._context.get('active_id')
active_id = self.env.context.get('active_id')
bve_view = self.env['bve.view'].browse(active_id) bve_view = self.env['bve.view'].browse(active_id)
menu = self.env['ir.ui.menu'].create({ menu = self.env['ir.ui.menu'].create({
'name': self.name, 'name': self.name,
@ -31,8 +31,8 @@ class WizardModelMenuCreate(models.TransientModel):
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
defaults = super(WizardModelMenuCreate, self).default_get(fields_list) defaults = super(WizardModelMenuCreate, self).default_get(fields_list)
if self._context.get('active_model') == 'bve.view':
active_id = self._context.get('active_id')
if self.env.context.get('active_model') == 'bve.view':
active_id = self.env.context.get('active_id')
bve_view = self.env['bve.view'].browse(active_id) bve_view = self.env['bve.view'].browse(active_id)
defaults.setdefault('name', bve_view.name) defaults.setdefault('name', bve_view.name)
return defaults return defaults
Loading…
Cancel
Save