[13.0][MIG] bi_view_editor
parent
f491cf2fab
commit
b5f53e2cc9
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import wizard
|
from . import wizard
|
||||||
from .hooks import post_load, uninstall_hook
|
from .hooks import uninstall_hook
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"website": "https://github.com/OCA/reporting-engine",
|
"website": "https://github.com/OCA/reporting-engine",
|
||||||
"category": "Reporting",
|
"category": "Reporting",
|
||||||
"version": "12.0.1.0.0",
|
"version": "13.0.1.0.0",
|
||||||
"development_status": "Beta",
|
"development_status": "Beta",
|
||||||
"depends": ["web",],
|
"depends": ["web"],
|
||||||
"data": [
|
"data": [
|
||||||
"security/ir.model.access.csv",
|
"security/ir.model.access.csv",
|
||||||
"security/rules.xml",
|
"security/rules.xml",
|
||||||
|
@ -19,7 +19,6 @@
|
||||||
"views/bve_view.xml",
|
"views/bve_view.xml",
|
||||||
],
|
],
|
||||||
"qweb": ["static/src/xml/bi_view_editor.xml"],
|
"qweb": ["static/src/xml/bi_view_editor.xml"],
|
||||||
"post_load": "post_load",
|
|
||||||
"uninstall_hook": "uninstall_hook",
|
"uninstall_hook": "uninstall_hook",
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,6 @@
|
||||||
# Copyright 2015-2019 Onestein (<https://www.onestein.eu>)
|
# Copyright 2015-2019 Onestein (<https://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, api, modules
|
|
||||||
from odoo.tools import existing_tables, topological_sort
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _bi_view(_name):
|
|
||||||
return _name.startswith("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
|
||||||
|
|
|
@ -60,7 +60,7 @@ class BveView(models.Model):
|
||||||
line_ids = self._sync_lines_and_data(bve_view.data)
|
line_ids = self._sync_lines_and_data(bve_view.data)
|
||||||
bve_view.write({"line_ids": line_ids})
|
bve_view.write({"line_ids": line_ids})
|
||||||
|
|
||||||
name = fields.Char(required=True, copy=False)
|
name = fields.Char(required=True, copy=False, default="")
|
||||||
model_name = fields.Char(compute="_compute_model_name", store=True)
|
model_name = fields.Char(compute="_compute_model_name", store=True)
|
||||||
note = fields.Text(string="Notes")
|
note = fields.Text(string="Notes")
|
||||||
state = fields.Selection(
|
state = fields.Selection(
|
||||||
|
@ -100,7 +100,7 @@ class BveView(models.Model):
|
||||||
)
|
)
|
||||||
query = fields.Text(compute="_compute_sql_query")
|
query = fields.Text(compute="_compute_sql_query")
|
||||||
over_condition = fields.Text(
|
over_condition = fields.Text(
|
||||||
states={"draft": [("readonly", False),],},
|
states={"draft": [("readonly", False)]},
|
||||||
readonly=True,
|
readonly=True,
|
||||||
help="Condition to be inserted in the OVER part "
|
help="Condition to be inserted in the OVER part "
|
||||||
"of the ID's row_number function.\n"
|
"of the ID's row_number function.\n"
|
||||||
|
@ -169,7 +169,7 @@ class BveView(models.Model):
|
||||||
try:
|
try:
|
||||||
png_base64_image = base64.b64encode(graph.create_png())
|
png_base64_image = base64.b64encode(graph.create_png())
|
||||||
bve_view.er_diagram_image = png_base64_image
|
bve_view.er_diagram_image = png_base64_image
|
||||||
except:
|
except Exception:
|
||||||
bve_view.er_diagram_image = False
|
bve_view.er_diagram_image = False
|
||||||
|
|
||||||
def _create_view_arch(self):
|
def _create_view_arch(self):
|
||||||
|
@ -191,7 +191,7 @@ class BveView(models.Model):
|
||||||
return '<field name="{}" {} />'.format(line.name, res)
|
return '<field name="{}" {} />'.format(line.name, res)
|
||||||
|
|
||||||
bve_field_lines = self.field_ids.filtered(lambda l: l.in_list)
|
bve_field_lines = self.field_ids.filtered(lambda l: l.in_list)
|
||||||
return list(map(_get_field_attrs, bve_field_lines))
|
return list(map(_get_field_attrs, bve_field_lines.sorted("sequence")))
|
||||||
|
|
||||||
def _create_bve_view(self):
|
def _create_bve_view(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
@ -236,7 +236,7 @@ class BveView(models.Model):
|
||||||
"model": self.model_name,
|
"model": self.model_name,
|
||||||
"priority": 16,
|
"priority": 16,
|
||||||
"arch": """<?xml version="1.0"?>
|
"arch": """<?xml version="1.0"?>
|
||||||
<search string="Search BI View">
|
<search>
|
||||||
{}
|
{}
|
||||||
</search>
|
</search>
|
||||||
""".format(
|
""".format(
|
||||||
|
@ -254,7 +254,7 @@ class BveView(models.Model):
|
||||||
"model": self.model_name,
|
"model": self.model_name,
|
||||||
"priority": 16,
|
"priority": 16,
|
||||||
"arch": """<?xml version="1.0"?>
|
"arch": """<?xml version="1.0"?>
|
||||||
<tree string="List Analysis" create="false">
|
<tree create="false">
|
||||||
{}
|
{}
|
||||||
</tree>
|
</tree>
|
||||||
""".format(
|
""".format(
|
||||||
|
@ -272,7 +272,6 @@ class BveView(models.Model):
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"res_model": self.model_name,
|
"res_model": self.model_name,
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "tree,graph,pivot",
|
"view_mode": "tree,graph,pivot",
|
||||||
"view_id": tree_view.id,
|
"view_id": tree_view.id,
|
||||||
"context": "{'service_name': '%s'}" % self.name,
|
"context": "{'service_name': '%s'}" % self.name,
|
||||||
|
@ -475,7 +474,7 @@ class BveView(models.Model):
|
||||||
self.env["ir.model.access"]
|
self.env["ir.model.access"]
|
||||||
.sudo()
|
.sudo()
|
||||||
.search(
|
.search(
|
||||||
[("model_id", "=", line_model.id), ("perm_read", "=", True),]
|
[("model_id", "=", line_model.id), ("perm_read", "=", True)]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
group_list = ""
|
group_list = ""
|
||||||
|
@ -495,20 +494,18 @@ class BveView(models.Model):
|
||||||
if not self.line_ids:
|
if not self.line_ids:
|
||||||
raise ValidationError(_("No data to process."))
|
raise ValidationError(_("No data to process."))
|
||||||
|
|
||||||
if any(not line.model_id for line in self.line_ids):
|
|
||||||
invalid_lines = self.line_ids.filtered(lambda l: not l.model_id)
|
invalid_lines = self.line_ids.filtered(lambda l: not l.model_id)
|
||||||
missing_models = set(invalid_lines.mapped("model_name"))
|
if invalid_lines:
|
||||||
missing_models = ", ".join(missing_models)
|
missing_models = ", ".join(set(invalid_lines.mapped("model_name")))
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(
|
_(
|
||||||
"Following models are missing: %s.\n"
|
"Following models are missing: %s.\n"
|
||||||
"Probably some modules were uninstalled." % (missing_models,)
|
"Probably some modules were uninstalled." % (missing_models,)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if any(not line.field_id for line in self.line_ids):
|
|
||||||
invalid_lines = self.line_ids.filtered(lambda l: not l.field_id)
|
invalid_lines = self.line_ids.filtered(lambda l: not l.field_id)
|
||||||
missing_fields = set(invalid_lines.mapped("field_name"))
|
if invalid_lines:
|
||||||
missing_fields = ", ".join(missing_fields)
|
missing_fields = ", ".join(set(invalid_lines.mapped("field_name")))
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Following fields are missing: {}.".format(missing_fields))
|
_("Following fields are missing: {}.".format(missing_fields))
|
||||||
)
|
)
|
||||||
|
@ -520,7 +517,6 @@ class BveView(models.Model):
|
||||||
action["display_name"] = _("BI View")
|
action["display_name"] = _("BI View")
|
||||||
return action
|
return action
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def copy(self, default=None):
|
def copy(self, default=None):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
default = dict(default or {}, name=_("%s (copy)") % self.name)
|
default = dict(default or {}, name=_("%s (copy)") % self.name)
|
||||||
|
@ -623,6 +619,8 @@ class BveView(models.Model):
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_clean_list(self, data_dict):
|
def get_clean_list(self, data_dict):
|
||||||
|
serialized_data = data_dict
|
||||||
|
if type(data_dict) == str:
|
||||||
serialized_data = json.loads(data_dict)
|
serialized_data = json.loads(data_dict)
|
||||||
table_alias_list = set()
|
table_alias_list = set()
|
||||||
for item in serialized_data:
|
for item in serialized_data:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2015-2019 Onestein (<https://www.onestein.eu>)
|
# Copyright 2015-2020 Onestein (<https://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).
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
|
@ -12,11 +12,11 @@ class BveViewLine(models.Model):
|
||||||
name = fields.Char(compute="_compute_name")
|
name = fields.Char(compute="_compute_name")
|
||||||
sequence = fields.Integer(default=1)
|
sequence = fields.Integer(default=1)
|
||||||
bve_view_id = fields.Many2one("bve.view", ondelete="cascade")
|
bve_view_id = fields.Many2one("bve.view", ondelete="cascade")
|
||||||
model_id = fields.Many2one("ir.model", string="Model")
|
model_id = fields.Many2one("ir.model")
|
||||||
model_name = fields.Char(compute="_compute_model_name", store=True)
|
model_name = fields.Char(related="model_id.model", store=True, string="Model Name")
|
||||||
table_alias = fields.Char()
|
table_alias = fields.Char(required=True)
|
||||||
join_model_id = fields.Many2one("ir.model", string="Join Model")
|
join_model_id = fields.Many2one("ir.model")
|
||||||
field_id = fields.Many2one("ir.model.fields", string="Field")
|
field_id = fields.Many2one("ir.model.fields")
|
||||||
field_name = fields.Char(compute="_compute_model_field_name", store=True)
|
field_name = fields.Char(compute="_compute_model_field_name", store=True)
|
||||||
ttype = fields.Char(string="Type")
|
ttype = fields.Char(string="Type")
|
||||||
description = fields.Char(translate=True)
|
description = fields.Char(translate=True)
|
||||||
|
@ -29,7 +29,7 @@ class BveViewLine(models.Model):
|
||||||
measure = fields.Boolean()
|
measure = fields.Boolean()
|
||||||
in_list = fields.Boolean()
|
in_list = fields.Boolean()
|
||||||
list_attr = fields.Selection(
|
list_attr = fields.Selection(
|
||||||
[("sum", "Sum"), ("avg", "Average"),], string="List Attribute", default="sum"
|
[("sum", "Sum"), ("avg", "Average")], string="List Attribute", default="sum"
|
||||||
)
|
)
|
||||||
view_field_type = fields.Char(compute="_compute_view_field_type")
|
view_field_type = fields.Char(compute="_compute_view_field_type")
|
||||||
|
|
||||||
|
@ -44,12 +44,11 @@ class BveViewLine(models.Model):
|
||||||
@api.constrains("row", "column", "measure")
|
@api.constrains("row", "column", "measure")
|
||||||
def _constrains_options_check(self):
|
def _constrains_options_check(self):
|
||||||
measure_types = ["float", "integer", "monetary"]
|
measure_types = ["float", "integer", "monetary"]
|
||||||
for line in self.filtered(lambda l: l.row or l.column):
|
lines = self.filtered(lambda l: l.join_model_id or l.ttype in measure_types)
|
||||||
if line.join_model_id or line.ttype in measure_types:
|
if lines.filtered(lambda l: l.row or l.column):
|
||||||
err_msg = _("This field cannot be a row or a column.")
|
err_msg = _("This field cannot be a row or a column.")
|
||||||
raise ValidationError(err_msg)
|
raise ValidationError(err_msg)
|
||||||
for line in self.filtered(lambda l: l.measure):
|
if lines.filtered(lambda l: l.measure):
|
||||||
if line.join_model_id or line.ttype not in measure_types:
|
|
||||||
err_msg = _("This field cannot be a measure.")
|
err_msg = _("This field cannot be a measure.")
|
||||||
raise ValidationError(err_msg)
|
raise ValidationError(err_msg)
|
||||||
|
|
||||||
|
@ -67,21 +66,17 @@ class BveViewLine(models.Model):
|
||||||
|
|
||||||
@api.depends("field_id", "sequence")
|
@api.depends("field_id", "sequence")
|
||||||
def _compute_name(self):
|
def _compute_name(self):
|
||||||
for line in self.filtered(lambda l: l.field_id):
|
for line in self:
|
||||||
field_name = line.field_id.name
|
line.name = False
|
||||||
line.name = "x_bve_{}_{}".format(line.table_alias, field_name)
|
if line.field_id:
|
||||||
|
line.name = "x_bve_{}_{}".format(line.table_alias, line.field_id.name)
|
||||||
@api.depends("model_id")
|
|
||||||
def _compute_model_name(self):
|
|
||||||
for line in self.filtered(lambda l: l.model_id):
|
|
||||||
line.model_name = line.model_id.model
|
|
||||||
|
|
||||||
@api.depends("field_id")
|
@api.depends("field_id")
|
||||||
def _compute_model_field_name(self):
|
def _compute_model_field_name(self):
|
||||||
for line in self.filtered(lambda l: l.field_id):
|
for line in self:
|
||||||
field_name = line.description
|
line.field_name = False
|
||||||
model_name = line.model_name
|
if line.field_id:
|
||||||
line.field_name = "{} ({})".format(field_name, model_name)
|
line.field_name = "{} ({})".format(line.description, line.model_name)
|
||||||
|
|
||||||
def _prepare_field_vals(self):
|
def _prepare_field_vals(self):
|
||||||
vals_list = []
|
vals_list = []
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from odoo import api, models, registry
|
from odoo import api, models
|
||||||
|
|
||||||
NO_BI_MODELS = ["fetchmail.server"]
|
NO_BI_MODELS = ["fetchmail.server"]
|
||||||
|
|
||||||
|
@ -212,25 +212,3 @@ class IrModel(models.Model):
|
||||||
)
|
)
|
||||||
fields_dict = list(map(dict_for_field, fields))
|
fields_dict = list(map(dict_for_field, fields))
|
||||||
return fields_dict
|
return fields_dict
|
||||||
|
|
||||||
@api.model
|
|
||||||
def create(self, vals):
|
|
||||||
if self.env.context and self.env.context.get("bve"):
|
|
||||||
vals["state"] = "base"
|
|
||||||
res = super().create(vals)
|
|
||||||
|
|
||||||
# this sql update is necessary since a write method here would
|
|
||||||
# be not working (an orm constraint is restricting the modification
|
|
||||||
# of the state field while updating ir.model)
|
|
||||||
q = "UPDATE ir_model SET state = 'manual' WHERE id = %s"
|
|
||||||
self.env.cr.execute(q, (res.id,))
|
|
||||||
|
|
||||||
# # update registry
|
|
||||||
if self.env.context.get("bve"):
|
|
||||||
# setup models; this reloads custom models in registry
|
|
||||||
self.pool.setup_models(self._cr)
|
|
||||||
|
|
||||||
# signal that registry has changed
|
|
||||||
registry(self.env.cr.dbname).signal_changes()
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ def _bi_view(_name):
|
||||||
_auto_init_orig = models.BaseModel._auto_init
|
_auto_init_orig = models.BaseModel._auto_init
|
||||||
|
|
||||||
|
|
||||||
@api.model_cr_context
|
|
||||||
def _auto_init(self):
|
def _auto_init(self):
|
||||||
|
|
||||||
# This monkey patch is meant to fix an error (probably
|
# This monkey patch is meant to fix an error (probably
|
||||||
|
|
|
@ -22,5 +22,8 @@ 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
|
possibilities for more advanced users, like to use LEFT JOIN instead of the
|
||||||
default INNER JOIN.
|
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.
|
It also possible to improve the IDs generation for new views by adding an
|
||||||
For instance, an ORDER BY clause helps preventing unreliable behavior when filtering the generated views.
|
`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.
|
||||||
|
|
|
@ -159,11 +159,6 @@
|
||||||
width: 175px;
|
width: 175px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.oe_form_field_bi_editor .context-menu .checkbox {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oe_form_field_bi_editor .context-menu .checkbox label {
|
.oe_form_field_bi_editor .context-menu .checkbox label {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,10 @@ odoo.define("bi_view_editor.FieldList", function(require) {
|
||||||
var core = require("web.core");
|
var core = require("web.core");
|
||||||
var qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
var Widget = require("web.Widget");
|
var Widget = require("web.Widget");
|
||||||
|
var mixins = require("web.mixins");
|
||||||
|
|
||||||
var FieldListContextMenu = Widget.extend({
|
var FieldListContextMenu = Widget.extend(
|
||||||
|
_.extend({}, mixins.EventDispatcherMixin, {
|
||||||
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() {
|
||||||
|
@ -22,13 +24,15 @@ odoo.define("bi_view_editor.FieldList", function(require) {
|
||||||
top: y + "px",
|
top: y + "px",
|
||||||
});
|
});
|
||||||
this.$el.removeClass("d-none");
|
this.$el.removeClass("d-none");
|
||||||
return _.extend({}, window.Backbone.Events);
|
return this;
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
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, $item) {
|
||||||
|
var field = $item.data("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);
|
||||||
|
@ -49,7 +53,7 @@ odoo.define("bi_view_editor.FieldList", function(require) {
|
||||||
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");
|
||||||
events.trigger("change", field);
|
events.trigger("change", field, $item);
|
||||||
});
|
});
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
|
@ -58,7 +62,8 @@ 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, $item) {
|
||||||
|
var node = $item.data("field");
|
||||||
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);
|
||||||
|
@ -83,8 +88,26 @@ odoo.define("bi_view_editor.FieldList", function(require) {
|
||||||
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);
|
||||||
|
this.contextmenu.on(
|
||||||
|
"change",
|
||||||
|
this,
|
||||||
|
function(f, $item) {
|
||||||
|
$item.data("field", f);
|
||||||
|
this.refreshItem($item);
|
||||||
|
this.trigger("updated");
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
this.contextmenu_join = new FieldListJoinContextMenu(this);
|
this.contextmenu_join = new FieldListJoinContextMenu(this);
|
||||||
this.contextmenu_join.appendTo(this.$el);
|
this.contextmenu_join.appendTo(this.$el);
|
||||||
|
this.contextmenu_join.on(
|
||||||
|
"change",
|
||||||
|
this,
|
||||||
|
function(f, $item) {
|
||||||
|
$item.data("field", f);
|
||||||
|
this.refreshItem($item);
|
||||||
|
this.trigger("updated");
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
this.$table = this.$el.find("tbody");
|
this.$table = this.$el.find("tbody");
|
||||||
this.mode = null;
|
this.mode = null;
|
||||||
return res;
|
return res;
|
||||||
|
@ -201,14 +224,7 @@ odoo.define("bi_view_editor.FieldList", function(require) {
|
||||||
if (field.join_node) {
|
if (field.join_node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
contextmenu.open(x - 20, y - 20, $item.data("field")).on(
|
contextmenu.open(x - 20, y - 20, $item);
|
||||||
"change",
|
|
||||||
function(f) {
|
|
||||||
$item.data("field", f);
|
|
||||||
this.refreshItem($item);
|
|
||||||
this.trigger("updated");
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
refreshItem: function($item) {
|
refreshItem: function($item) {
|
||||||
var data = $item.data("field");
|
var data = $item.data("field");
|
||||||
|
|
|
@ -80,7 +80,7 @@ odoo.define("bi_view_editor.ModelList", function(require) {
|
||||||
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).then(function(fields) {
|
||||||
self.populateFields(fields, model.id);
|
self.populateFields(fields, model.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ odoo.define("bi_view_editor.ModelList", function(require) {
|
||||||
this.removeAsActive(model.id);
|
this.removeAsActive(model.id);
|
||||||
} else {
|
} else {
|
||||||
this.addAsActive(model.id);
|
this.addAsActive(model.id);
|
||||||
this.loadFields(model.id).done(
|
this.loadFields(model.id).then(
|
||||||
function(fields) {
|
function(fields) {
|
||||||
this.populateFields(fields, model.id);
|
this.populateFields(fields, model.id);
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
|
|
|
@ -30,10 +30,10 @@ odoo.define("bi_view_editor", function(require) {
|
||||||
|
|
||||||
// Init FieldList
|
// Init FieldList
|
||||||
this.field_list = new FieldList(this);
|
this.field_list = new FieldList(this);
|
||||||
this.field_list.appendTo(this.$(".body > .right"));
|
this.field_list.appendTo(this.$(".body > .right")).then(
|
||||||
|
function() {
|
||||||
this.field_list.on("removed", this, this.fieldListRemoved);
|
this.field_list.on("removed", this, this.fieldListRemoved);
|
||||||
this.field_list.on("updated", this, this.fieldListChanged);
|
this.field_list.on("updated", this, this.fieldListChanged);
|
||||||
|
|
||||||
this.$el.find(".body > .right").droppable({
|
this.$el.find(".body > .right").droppable({
|
||||||
accept: "div.class-list div.field",
|
accept: "div.class-list div.field",
|
||||||
drop: function(event, ui) {
|
drop: function(event, ui) {
|
||||||
|
@ -48,6 +48,9 @@ odoo.define("bi_view_editor", function(require) {
|
||||||
this.renderValue();
|
this.renderValue();
|
||||||
this.loadAndPopulateModelList();
|
this.loadAndPopulateModelList();
|
||||||
this.updateMode();
|
this.updateMode();
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
clear: function() {
|
clear: function() {
|
||||||
|
@ -61,10 +64,9 @@ odoo.define("bi_view_editor", function(require) {
|
||||||
this._setValue(this.field_list.get());
|
this._setValue(this.field_list.get());
|
||||||
},
|
},
|
||||||
fieldListRemoved: function() {
|
fieldListRemoved: function() {
|
||||||
console.log(this.field_list.get());
|
|
||||||
this._setValue(this.field_list.get());
|
this._setValue(this.field_list.get());
|
||||||
var model = new Data.DataSet(this, "bve.view");
|
var model = new Data.DataSet(this, "bve.view");
|
||||||
model.call("get_clean_list", [this.value]).then(
|
model.call("get_clean_list", [this.lastSetValue]).then(
|
||||||
function(result) {
|
function(result) {
|
||||||
this.field_list.set(JSON.parse(result));
|
this.field_list.set(JSON.parse(result));
|
||||||
this._setValue(this.field_list.get());
|
this._setValue(this.field_list.get());
|
||||||
|
@ -91,7 +93,7 @@ odoo.define("bi_view_editor", function(require) {
|
||||||
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(
|
this.model_list.loadModels(model_ids).then(
|
||||||
function(models) {
|
function(models) {
|
||||||
this.model_list.populateModels(models);
|
this.model_list.populateModels(models);
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
|
|
|
@ -120,18 +120,16 @@
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
data-for="column"
|
data-for="column"
|
||||||
class="checkbox-column"
|
class="checkbox-column"
|
||||||
/> Column
|
/>
|
||||||
|
<span>Column</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input type="checkbox" data-for="row" class="checkbox-row" />
|
||||||
type="checkbox"
|
<span>Row</span>
|
||||||
data-for="row"
|
|
||||||
class="checkbox-row"
|
|
||||||
/> Row
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -142,18 +140,16 @@
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
data-for="measure"
|
data-for="measure"
|
||||||
class="checkbox-measure"
|
class="checkbox-measure"
|
||||||
/> Measure
|
/>
|
||||||
|
<span>Measure</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input type="checkbox" data-for="list" class="checkbox-list" />
|
||||||
type="checkbox"
|
<span>List</span>
|
||||||
data-for="list"
|
|
||||||
class="checkbox-list"
|
|
||||||
/> List
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -8,7 +8,7 @@ from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase
|
||||||
from odoo.tools import mute_logger
|
from odoo.tools import mute_logger
|
||||||
|
|
||||||
from ..hooks import post_load, uninstall_hook
|
from ..hooks import uninstall_hook
|
||||||
|
|
||||||
|
|
||||||
class TestBiViewEditor(TransactionCase):
|
class TestBiViewEditor(TransactionCase):
|
||||||
|
@ -174,9 +174,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
def test_06_create_group_bve_object(self):
|
def test_06_create_group_bve_object(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
employees_group = self.env.ref("base.group_user")
|
employees_group = self.env.ref("base.group_user")
|
||||||
vals.update(
|
vals.update({"name": "Test View2", "group_ids": [(6, 0, [employees_group.id])]})
|
||||||
{"name": "Test View2", "group_ids": [(6, 0, [employees_group.id])],}
|
|
||||||
)
|
|
||||||
|
|
||||||
bi_view2 = self.env["bve.view"].create(vals)
|
bi_view2 = self.env["bve.view"].create(vals)
|
||||||
self.assertEqual(len(bi_view2.user_ids), len(employees_group.users))
|
self.assertEqual(len(bi_view2.user_ids), len(employees_group.users))
|
||||||
|
@ -200,9 +198,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
def test_09_create_open_bve_object(self):
|
def test_09_create_open_bve_object(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
employees_group = self.env.ref("base.group_user")
|
employees_group = self.env.ref("base.group_user")
|
||||||
vals.update(
|
vals.update({"name": "Test View4", "group_ids": [(6, 0, [employees_group.id])]})
|
||||||
{"name": "Test View4", "group_ids": [(6, 0, [employees_group.id])],}
|
|
||||||
)
|
|
||||||
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)
|
||||||
self.assertEqual(len(bi_view.line_ids), 3)
|
self.assertEqual(len(bi_view.line_ids), 3)
|
||||||
|
@ -251,9 +247,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
@odoo.tests.tagged("post_install", "-at_install")
|
@odoo.tests.tagged("post_install", "-at_install")
|
||||||
def test_10_create_open_bve_object_apostrophe(self):
|
def test_10_create_open_bve_object_apostrophe(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
vals.update(
|
vals.update({"name": "Test View5"})
|
||||||
{"name": "Test View5",}
|
|
||||||
)
|
|
||||||
data_list = 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"
|
||||||
|
@ -310,9 +304,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
def test_12_check_groups(self):
|
def test_12_check_groups(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
group_system = self.env.ref("base.group_system")
|
group_system = self.env.ref("base.group_system")
|
||||||
vals.update(
|
vals.update({"name": "Test View1", "group_ids": [(6, 0, [group_system.id])]})
|
||||||
{"name": "Test View1", "group_ids": [(6, 0, [group_system.id])],}
|
|
||||||
)
|
|
||||||
bi_view1 = self.env["bve.view"].create(vals)
|
bi_view1 = self.env["bve.view"].create(vals)
|
||||||
with self.assertRaises(UserError):
|
with self.assertRaises(UserError):
|
||||||
bi_view1.action_create()
|
bi_view1.action_create()
|
||||||
|
@ -320,9 +312,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
def test_13_check_lines_missing_model(self):
|
def test_13_check_lines_missing_model(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
group_user = self.env.ref("base.group_user")
|
group_user = self.env.ref("base.group_user")
|
||||||
vals.update(
|
vals.update({"name": "Test View1", "group_ids": [(6, 0, [group_user.id])]})
|
||||||
{"name": "Test View1", "group_ids": [(6, 0, [group_user.id])],}
|
|
||||||
)
|
|
||||||
bi_view1 = self.env["bve.view"].create(vals)
|
bi_view1 = self.env["bve.view"].create(vals)
|
||||||
for line in bi_view1.line_ids:
|
for line in bi_view1.line_ids:
|
||||||
self.assertTrue(line.model_id)
|
self.assertTrue(line.model_id)
|
||||||
|
@ -338,9 +328,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
def test_14_check_lines_missing_fieldl(self):
|
def test_14_check_lines_missing_fieldl(self):
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
group_user = self.env.ref("base.group_user")
|
group_user = self.env.ref("base.group_user")
|
||||||
vals.update(
|
vals.update({"name": "Test View1", "group_ids": [(6, 0, [group_user.id])]})
|
||||||
{"name": "Test View1", "group_ids": [(6, 0, [group_user.id])],}
|
|
||||||
)
|
|
||||||
bi_view1 = self.env["bve.view"].create(vals)
|
bi_view1 = self.env["bve.view"].create(vals)
|
||||||
for line in bi_view1.line_ids:
|
for line in bi_view1.line_ids:
|
||||||
self.assertTrue(line.field_id)
|
self.assertTrue(line.field_id)
|
||||||
|
@ -362,9 +350,6 @@ class TestBiViewEditor(TransactionCase):
|
||||||
self.assertTrue(data)
|
self.assertTrue(data)
|
||||||
self.assertTrue(isinstance(data, list))
|
self.assertTrue(isinstance(data, list))
|
||||||
|
|
||||||
def test_16_post_load(self):
|
|
||||||
post_load()
|
|
||||||
|
|
||||||
def test_17_uninstall_hook(self):
|
def test_17_uninstall_hook(self):
|
||||||
uninstall_hook(self.cr, self.env)
|
uninstall_hook(self.cr, self.env)
|
||||||
|
|
||||||
|
@ -419,9 +404,7 @@ class TestBiViewEditor(TransactionCase):
|
||||||
ERROR: bad_query line in the logs.
|
ERROR: bad_query line in the logs.
|
||||||
"""
|
"""
|
||||||
vals = self.bi_view1_vals
|
vals = self.bi_view1_vals
|
||||||
vals.update(
|
vals.update({"name": "Test View broken", "over_condition": "bad SQL code"})
|
||||||
{"name": "Test View broken", "over_condition": "bad SQL code",}
|
|
||||||
)
|
|
||||||
bi_view = self.env["bve.view"].create(vals)
|
bi_view = self.env["bve.view"].create(vals)
|
||||||
with self.assertRaises(UserError) as ue:
|
with self.assertRaises(UserError) as ue:
|
||||||
bi_view.action_create()
|
bi_view.action_create()
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="action_bi_view_editor_translations" model="ir.actions.act_window">
|
|
||||||
<field name="name">Translations</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">ir.translation</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field
|
|
||||||
name="domain"
|
|
||||||
>[('res_id', '=', active_record.), ('name', '=', 'ir.model.fields,field_description')]</field>
|
|
||||||
<field name="view_id" ref="base.view_translation_dialog_tree" />
|
|
||||||
</record>
|
|
||||||
<record id="view_bi_view_editor_view_tree" model="ir.ui.view">
|
<record id="view_bi_view_editor_view_tree" model="ir.ui.view">
|
||||||
<field name="model">bve.view</field>
|
<field name="model">bve.view</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Custom BI View">
|
<tree>
|
||||||
<field name="name" />
|
<field name="name" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
@ -21,7 +11,7 @@
|
||||||
<record id="view_bi_view_editor_view_form" model="ir.ui.view">
|
<record id="view_bi_view_editor_view_form" model="ir.ui.view">
|
||||||
<field name="model">bve.view</field>
|
<field name="model">bve.view</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Custom Object">
|
<form>
|
||||||
<header>
|
<header>
|
||||||
<button
|
<button
|
||||||
name="action_reset"
|
name="action_reset"
|
||||||
|
@ -108,6 +98,7 @@
|
||||||
<tree
|
<tree
|
||||||
editable="bottom"
|
editable="bottom"
|
||||||
decoration-muted="in_list == False"
|
decoration-muted="in_list == False"
|
||||||
|
create="false"
|
||||||
>
|
>
|
||||||
<field name="sequence" widget="handle" />
|
<field name="sequence" widget="handle" />
|
||||||
<field name="description" string="Field" />
|
<field name="description" string="Field" />
|
||||||
|
@ -142,7 +133,7 @@
|
||||||
name="relation_ids"
|
name="relation_ids"
|
||||||
attrs="{'readonly': [('state','=','created')]}"
|
attrs="{'readonly': [('state','=','created')]}"
|
||||||
>
|
>
|
||||||
<tree editable="bottom">
|
<tree editable="bottom" create="false">
|
||||||
<field name="sequence" widget="handle" />
|
<field name="sequence" widget="handle" />
|
||||||
<field name="description" string="Field" />
|
<field name="description" string="Field" />
|
||||||
<field name="model_id" readonly="1" />
|
<field name="model_id" readonly="1" />
|
||||||
|
@ -177,7 +168,6 @@
|
||||||
<record id="action_bi_view_editor_view_form" model="ir.actions.act_window">
|
<record id="action_bi_view_editor_view_form" model="ir.actions.act_window">
|
||||||
<field name="name">Custom BI Views</field>
|
<field name="name">Custom BI Views</field>
|
||||||
<field name="res_model">bve.view</field>
|
<field name="res_model">bve.view</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="oe_view_nocontent_create">
|
<p class="oe_view_nocontent_create">
|
||||||
|
|
Loading…
Reference in New Issue