tarteo
7 years ago
committed by
Pedro M. Baeza
90 changed files with 4375 additions and 9400 deletions
-
21bi_view_editor/README.rst
-
4bi_view_editor/__init__.py
-
11bi_view_editor/__manifest__.py
-
5bi_view_editor/hooks.py
-
174bi_view_editor/i18n/am.po
-
174bi_view_editor/i18n/ar.po
-
174bi_view_editor/i18n/bg.po
-
174bi_view_editor/i18n/bs.po
-
174bi_view_editor/i18n/ca.po
-
174bi_view_editor/i18n/cs.po
-
174bi_view_editor/i18n/da.po
-
174bi_view_editor/i18n/de.po
-
174bi_view_editor/i18n/el_GR.po
-
174bi_view_editor/i18n/en_GB.po
-
200bi_view_editor/i18n/es.po
-
174bi_view_editor/i18n/es_AR.po
-
174bi_view_editor/i18n/es_CL.po
-
174bi_view_editor/i18n/es_CO.po
-
174bi_view_editor/i18n/es_CR.po
-
174bi_view_editor/i18n/es_DO.po
-
174bi_view_editor/i18n/es_EC.po
-
174bi_view_editor/i18n/es_ES.po
-
174bi_view_editor/i18n/es_MX.po
-
174bi_view_editor/i18n/es_PE.po
-
174bi_view_editor/i18n/es_PY.po
-
174bi_view_editor/i18n/es_VE.po
-
174bi_view_editor/i18n/et.po
-
174bi_view_editor/i18n/eu.po
-
174bi_view_editor/i18n/fa.po
-
174bi_view_editor/i18n/fi.po
-
214bi_view_editor/i18n/fr.po
-
174bi_view_editor/i18n/fr_CA.po
-
174bi_view_editor/i18n/fr_CH.po
-
174bi_view_editor/i18n/gl.po
-
174bi_view_editor/i18n/gl_ES.po
-
174bi_view_editor/i18n/he.po
-
179bi_view_editor/i18n/hr.po
-
175bi_view_editor/i18n/hr_HR.po
-
174bi_view_editor/i18n/hu.po
-
174bi_view_editor/i18n/id.po
-
174bi_view_editor/i18n/it.po
-
174bi_view_editor/i18n/ja.po
-
174bi_view_editor/i18n/ko.po
-
174bi_view_editor/i18n/lt.po
-
174bi_view_editor/i18n/lt_LT.po
-
174bi_view_editor/i18n/lv.po
-
174bi_view_editor/i18n/mk.po
-
174bi_view_editor/i18n/mn.po
-
174bi_view_editor/i18n/nb.po
-
174bi_view_editor/i18n/nb_NO.po
-
195bi_view_editor/i18n/nl.po
-
174bi_view_editor/i18n/nl_BE.po
-
285bi_view_editor/i18n/nl_NL.po
-
176bi_view_editor/i18n/pl.po
-
174bi_view_editor/i18n/pt.po
-
175bi_view_editor/i18n/pt_BR.po
-
174bi_view_editor/i18n/pt_PT.po
-
174bi_view_editor/i18n/ro.po
-
174bi_view_editor/i18n/ru.po
-
174bi_view_editor/i18n/sk.po
-
192bi_view_editor/i18n/sl.po
-
174bi_view_editor/i18n/sr.po
-
174bi_view_editor/i18n/sr@latin.po
-
174bi_view_editor/i18n/sv.po
-
174bi_view_editor/i18n/th.po
-
174bi_view_editor/i18n/tr.po
-
187bi_view_editor/i18n/tr_TR.po
-
174bi_view_editor/i18n/uk.po
-
176bi_view_editor/i18n/vi.po
-
174bi_view_editor/i18n/vi_VN.po
-
174bi_view_editor/i18n/zh_CN.po
-
174bi_view_editor/i18n/zh_TW.po
-
4bi_view_editor/models/__init__.py
-
68bi_view_editor/models/bve_view.py
-
21bi_view_editor/models/ir_model.py
-
132bi_view_editor/models/models.py
-
33bi_view_editor/static/src/css/bve.css
-
305bi_view_editor/static/src/js/bi_view_editor.FieldList.js
-
52bi_view_editor/static/src/js/bi_view_editor.JoinNodeDialog.js
-
171bi_view_editor/static/src/js/bi_view_editor.ModelList.js
-
143bi_view_editor/static/src/js/bi_view_editor.js
-
445bi_view_editor/static/src/js/bve.js
-
183bi_view_editor/static/src/xml/bi_view_editor.xml
-
8bi_view_editor/templates/assets_template.xml
-
54bi_view_editor/templates/qweb_template.xml
-
4bi_view_editor/tests/__init__.py
-
5bi_view_editor/tests/test_bi_view.py
-
19bi_view_editor/views/bve_view.xml
-
4bi_view_editor/wizard/__init__.py
-
5bi_view_editor/wizard/wizard_ir_model_menu_create.py
@ -1,6 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2015-2017 Onestein (<http://www.onestein.eu>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import models |
|||
from . import wizard |
|||
|
@ -1,285 +0,0 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * bi_view_editor |
|||
# |
|||
# Translators: |
|||
# Peter Hageman <hageman.p@gmail.com>, 2018 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2018-01-25 03:45+0000\n" |
|||
"PO-Revision-Date: 2018-01-25 03:45+0000\n" |
|||
"Last-Translator: Peter Hageman <hageman.p@gmail.com>, 2018\n" |
|||
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/teams/23907/nl_NL/)\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Language: nl_NL\n" |
|||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/bve_view.py:382 |
|||
#, python-format |
|||
msgid "%s (copy)" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:32 |
|||
#, python-format |
|||
msgid "<b>Join</b> using the field" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_action_id |
|||
msgid "Action" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/bve_view.py:376 |
|||
#, python-format |
|||
msgid "BI View" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model,name:bi_view_editor.model_bve_view |
|||
msgid "BI View Editor" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:43 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:41 |
|||
#, python-format |
|||
msgid "Choose join node" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.actions.act_window,help:bi_view_editor.action_bi_view_editor_view_form |
|||
msgid "Click to create a Custom Query Object." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Create a Menu" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: selection:bve.view,state:0 |
|||
msgid "Created" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_create_uid |
|||
msgid "Created by" |
|||
msgstr "Aangemaakt door" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_create_date |
|||
msgid "Created on" |
|||
msgstr "Aangemaakt op" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_tree |
|||
msgid "Custom BI View" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/bve_view.py:63 sql_constraint:bve.view:0 |
|||
#, python-format |
|||
msgid "Custom BI View names must be unique!" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.actions.act_window,name:bi_view_editor.action_bi_view_editor_view_form |
|||
#: model:ir.ui.menu,name:bi_view_editor.menu_bi_view_editor_view |
|||
msgid "Custom BI Views" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Custom Object" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.menu,name:bi_view_editor.menu_bi_view_editor_custom_reports |
|||
msgid "Custom Reports" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_data |
|||
msgid "Data" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_display_name |
|||
msgid "Display Name" |
|||
msgstr "Weergavenaam" |
|||
|
|||
#. module: bi_view_editor |
|||
#: selection:bve.view,state:0 |
|||
msgid "Draft" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Generate BI View" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_group_ids |
|||
msgid "Groups" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_model_name |
|||
msgid "Model name" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model,name:bi_view_editor.model_ir_model |
|||
msgid "Models" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_name |
|||
msgid "Name" |
|||
msgstr "Naam" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/models.py:42 |
|||
#, python-format |
|||
msgid "No data to be displayed." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/bve_view.py:285 |
|||
#, python-format |
|||
msgid "No data to process." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_note |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Notes" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Open BI View" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Query" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Reset to Draft" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.ui.view,arch_db:bi_view_editor.view_bi_view_editor_view_form |
|||
msgid "Security" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_state |
|||
msgid "State" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:26 |
|||
#, python-format |
|||
msgid "Use the field on model" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,help:bi_view_editor.field_bve_view_data |
|||
msgid "" |
|||
"Use the special query builder to define the query to generate your report " |
|||
"dataset. NOTE: To be edited, the query should be in 'Draft' status." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,help:bi_view_editor.field_bve_view_group_ids |
|||
msgid "" |
|||
"User groups allowed to see the generated report; if NO groups are specified " |
|||
"the report will be public for everyone." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_user_ids |
|||
msgid "Users" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model.fields,field_description:bi_view_editor.field_bve_view_view_id |
|||
msgid "View" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: code:addons/bi_view_editor/models/bve_view.py:420 |
|||
#, python-format |
|||
msgid "You cannot delete a created view! Reset the view to draft first." |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model,name:bi_view_editor.model_base |
|||
msgid "base" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:32 |
|||
#, python-format |
|||
msgid "model" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:30 |
|||
#, python-format |
|||
msgid "new" |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#. openerp-web |
|||
#: code:addons/bi_view_editor/static/src/js/bve.js:32 |
|||
#, python-format |
|||
msgid "on " |
|||
msgstr "" |
|||
|
|||
#. module: bi_view_editor |
|||
#: model:ir.model,name:bi_view_editor.model_wizard_ir_model_menu_create |
|||
msgid "wizard.ir.model.menu.create" |
|||
msgstr "" |
@ -1,6 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2015-2017 Onestein (<http://www.onestein.eu>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import models |
|||
from . import bve_view |
|||
|
@ -0,0 +1,305 @@ |
|||
/* Copyright 2015-2018 Onestein (<http://www.onestein.eu>) |
|||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
|||
|
|||
odoo.define('bi_view_editor.FieldList', function(require) { |
|||
"use strict"; |
|||
|
|||
var core = require('web.core'); |
|||
var qweb = core.qweb; |
|||
var Widget = require('web.Widget'); |
|||
|
|||
var FieldListContextMenu = Widget.extend({ |
|||
start: function() { |
|||
var res = this._super.apply(this, arguments); |
|||
this.$el.mouseleave(function() { |
|||
$(this).addClass('hidden'); |
|||
}); |
|||
return res; |
|||
}, |
|||
open: function(x, y) { |
|||
this.$el.css({ |
|||
'left': x + 'px', |
|||
'top': y + 'px' |
|||
}); |
|||
this.$el.removeClass('hidden'); |
|||
return _.extend({}, window.Backbone.Events); |
|||
} |
|||
}); |
|||
|
|||
var FieldListFieldContextMenu = FieldListContextMenu.extend({ |
|||
template: 'bi_view_editor.FieldList.FieldContextMenu', |
|||
open: function(x, y, field) { |
|||
this.$el.find('.checkbox-column').prop('checked', field.column); |
|||
this.$el.find('.checkbox-row').prop('checked', field.row); |
|||
this.$el.find('.checkbox-measure').prop('checked', field.measure); |
|||
this.$el.find('.checkbox-list').prop('checked', field.list); |
|||
|
|||
var measureable = |
|||
field.type === "float" || |
|||
field.type === "integer" || |
|||
field.type === "monetary" |
|||
; |
|||
|
|||
this.$el.find('.checkbox-column').attr('disabled', measureable); |
|||
this.$el.find('.checkbox-row').attr('disabled', measureable); |
|||
this.$el.find('.checkbox-measure').attr('disabled', !measureable); |
|||
this.$el.find('.checkbox-list').attr('disabled', false); |
|||
|
|||
var events = this._super(x, y, field); |
|||
this.$el.find('input').unbind('change'); |
|||
this.$el.find('input').change(function() { |
|||
var $checkbox = $(this); |
|||
var property = $checkbox.attr('data-for'); |
|||
field[property] = $checkbox.is(':checked'); |
|||
events.trigger('change', field); |
|||
}); |
|||
|
|||
return events; |
|||
} |
|||
}); |
|||
|
|||
var FieldListJoinContextMenu = FieldListContextMenu.extend({ |
|||
template: 'bi_view_editor.FieldList.JoinContextMenu', |
|||
open: function(x, y, node) { |
|||
this.$el.find('.checkbox-join-left').prop('checked', node.join_left); |
|||
|
|||
var events = this._super(x, y, node); |
|||
this.$el.find('input').unbind('change'); |
|||
this.$el.find('input').change(function() { |
|||
var $checkbox = $(this); |
|||
var property = $checkbox.attr('data-for'); |
|||
node[property] = $checkbox.is(':checked'); |
|||
events.trigger('change', node); |
|||
}); |
|||
return events; |
|||
} |
|||
}); |
|||
|
|||
var FieldList = Widget.extend({ |
|||
template: 'bi_view_editor.FieldList', |
|||
events: { |
|||
'click .delete-button': 'removeClicked', |
|||
'keyup input[name="description"]': 'keyupDescription' |
|||
}, |
|||
start: function() { |
|||
var res = this._super.apply(this, arguments); |
|||
this.contextmenu = new FieldListFieldContextMenu(this); |
|||
this.contextmenu.appendTo(this.$el); |
|||
this.contextmenu_join = new FieldListJoinContextMenu(this); |
|||
this.contextmenu_join.appendTo(this.$el); |
|||
this.$table = this.$el.find('tbody'); |
|||
this.mode = null; |
|||
return res; |
|||
}, |
|||
setMode: function(mode) { |
|||
if(mode === 'readonly') { |
|||
this.$el.find('input[type="text"]').attr('disabled', true); |
|||
this.$el.find(".delete-button:last").addClass('hidden'); |
|||
} else { |
|||
this.$el.find('input[type="text"]').removeAttr('disabled'); |
|||
this.$el.find(".delete-button:last").removeClass('hidden'); |
|||
} |
|||
this.mode = mode; |
|||
}, |
|||
get: function() { |
|||
return $.makeArray(this.$el.find("tbody tr").map(function () { |
|||
var field = $(this).data('field'); |
|||
field.description = $(this).find('input[name="description"]').val(); |
|||
return field; |
|||
})); |
|||
}, |
|||
getModelIds: function () { |
|||
var model_ids = {}; |
|||
this.$el.find("tbody tr").each(function () { |
|||
var data = $(this).data('field'); |
|||
model_ids[data.table_alias] = data.model_id; |
|||
}); |
|||
return model_ids; |
|||
}, |
|||
getModelData: function () { |
|||
var model_data = {}; |
|||
this.$el.find("tbody tr").each(function () { |
|||
var data = $(this).data('field'); |
|||
model_data[data.table_alias] = { |
|||
model_id: data.model_id, |
|||
model_name: data.model_name |
|||
}; |
|||
}); |
|||
return model_data; |
|||
}, |
|||
add: function(field) { |
|||
var self = this; |
|||
field.row = typeof field.row === 'undefined' ? false : field.row; |
|||
field.column = typeof field.column === 'undefined' ? false : field.column; |
|||
field.measure = typeof field.measure === 'undefined' ? false : field.measure; |
|||
field.list = typeof field.list === 'undefined' ? false : field.list; |
|||
field._id = typeof field._id === 'undefined' ? _.uniqueId('node_') : field._id; |
|||
if(field.join_node) { |
|||
field.join_left = typeof field.join_left === 'undefined' ? false : field.join_left; |
|||
} |
|||
|
|||
var i = 0; |
|||
var name = field.name; |
|||
while (this.get().filter(function(item) { |
|||
return item.name === field.name; |
|||
}).length > 0) { |
|||
field.name = name + '_' + i; |
|||
i++; |
|||
} |
|||
|
|||
// Render table row
|
|||
var $html = $(qweb.render(field.join_node ? 'bi_view_editor.JoinListItem' : 'bi_view_editor.FieldListItem', { |
|||
'field': field |
|||
})).data('field', field).contextmenu(function(e) { |
|||
var $item = $(this); |
|||
if(self.mode === 'readonly') { |
|||
return; |
|||
} |
|||
e.preventDefault(); |
|||
self.openContextMenu($item, e.pageX, e.pageY); |
|||
}); |
|||
|
|||
this.$el.find('tbody').append($html); |
|||
|
|||
this.$el.find(".delete-button").addClass('hidden'); |
|||
this.$el.find(".delete-button:last").removeClass('hidden'); |
|||
this.order(); |
|||
}, |
|||
remove: function(id) { |
|||
var $item = this.$el.find('tr[data-id="' + id + '"]'); |
|||
$item.remove(); |
|||
this.cleanJoinNodes(); |
|||
this.$el.find(".delete-button").addClass('hidden'); |
|||
this.$el.find(".delete-button:last").removeClass('hidden'); |
|||
this.trigger('removed', id); |
|||
}, |
|||
set: function(fields) { |
|||
var set_fields = fields; |
|||
if (!set_fields) { |
|||
set_fields = []; |
|||
} |
|||
this.$el.find('tbody tr').remove(); |
|||
for(var i = 0; i < set_fields.length; i++) { |
|||
this.add(set_fields[i]); |
|||
} |
|||
this.$el.find(".delete-button").addClass('hidden'); |
|||
this.$el.find(".delete-button:last").removeClass('hidden'); |
|||
}, |
|||
openContextMenu: function($item, x, y) { |
|||
var field = $item.data('field'); |
|||
var contextmenu = field.join_node ? this.contextmenu_join : this.contextmenu; |
|||
// Temporary disable contextmenu for join node (until left join is implemented)
|
|||
if (field.join_node) { |
|||
return; |
|||
} |
|||
contextmenu.open(x - 20, y - 20, $item.data('field')).on('change', function(f) { |
|||
$item.data('field', f); |
|||
this.refreshItem($item); |
|||
this.trigger('updated'); |
|||
}.bind(this)); |
|||
}, |
|||
refreshItem: function($item) { |
|||
var data = $item.data('field'); |
|||
var $attributes = $item.find('span[data-for], img[data-for]'); |
|||
$.each($attributes, function() { |
|||
var $attribute = $(this); |
|||
var value = data[$attribute.attr('data-for')]; |
|||
if(value) { |
|||
$attribute.removeClass('hidden'); |
|||
} else { |
|||
$attribute.addClass('hidden'); |
|||
} |
|||
}); |
|||
}, |
|||
removeClicked: function(e) { |
|||
var $button = $(e.currentTarget); |
|||
var id = $button.attr('data-id'); |
|||
this.remove(id); |
|||
}, |
|||
keyupDescription: function() { |
|||
this.trigger('updated'); |
|||
}, |
|||
cleanJoinNodes: function() { |
|||
var aliases = $.makeArray(this.$el.find("tbody tr").map(function () { |
|||
var data = $(this).data('field'); |
|||
return data.table_alias.localeCompare(data.join_node) > 0 ? data.join_node : data.table_alias; |
|||
})); |
|||
|
|||
this.$el.find("tbody tr").each(function () { |
|||
var data = $(this).data('field'); |
|||
if (typeof data.join_node === 'undefined') { |
|||
return; |
|||
} |
|||
var no_alias = data.table_alias.localeCompare(data.join_node) > 0 && |
|||
aliases.indexOf(data.table_alias) === -1; |
|||
if (no_alias || |
|||
aliases.indexOf(data.join_node) === -1) { |
|||
$(this).remove(); |
|||
} |
|||
}); |
|||
}, |
|||
getOrder: function() { |
|||
var items = this.get(); |
|||
var ordered = items.sort(function(a, b) { |
|||
var res = a.table_alias.localeCompare(b.table_alias); |
|||
if (res === 0) { |
|||
var both_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) { |
|||
return 0; |
|||
} else if(!a.join_node && b.join_node) { |
|||
if(b.table_alias.localeCompare(b.join_node) > 0) { |
|||
return 1; |
|||
} |
|||
return -1; |
|||
} else if(a.join_node && !b.join_node) { |
|||
if(a.table_alias.localeCompare(a.join_node) > 0) { |
|||
return -1; |
|||
} |
|||
return 1; |
|||
} |
|||
} |
|||
return res; |
|||
}); |
|||
|
|||
var res = []; |
|||
_.each(ordered, function(item) { |
|||
var already_exists = _.findIndex(res, function(f) { |
|||
return f._id === item._id; |
|||
}) !== -1; |
|||
if(already_exists) { |
|||
return; |
|||
} |
|||
res.push(item); |
|||
if(item.join_node) { |
|||
var join_node_fields = _.filter(ordered, function(f) { |
|||
return f.table_alias === item.join_node && !f.join_node; |
|||
}); |
|||
res = _.union(res, join_node_fields); |
|||
} |
|||
}); |
|||
return res; |
|||
}, |
|||
order: function() { |
|||
var order = this.getOrder(); |
|||
var $rows = this.$el.find("tbody tr"); |
|||
|
|||
$rows.sort(function(a, b) { |
|||
var a_index = _.findIndex(order, function(item) { |
|||
return item._id === $(a).data('field')._id; |
|||
}); |
|||
var b_index = _.findIndex(order, function(item) { |
|||
return item._id === $(b).data('field')._id; |
|||
}); |
|||
return a_index - b_index; |
|||
}).appendTo(this.$el.find("tbody")); |
|||
} |
|||
}); |
|||
|
|||
return { |
|||
'FieldList': FieldList, |
|||
'FieldListContextMenu': FieldListContextMenu, |
|||
'FieldListFieldContextMenu': FieldListFieldContextMenu, |
|||
'FieldListJoinContextMenu': FieldListJoinContextMenu |
|||
}; |
|||
}); |
@ -0,0 +1,52 @@ |
|||
/* Copyright 2015-2018 Onestein (<http://www.onestein.eu>) |
|||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
|||
|
|||
odoo.define('bi_view_editor.JoinNodeDialog', function (require) { |
|||
"use strict"; |
|||
|
|||
var Dialog = require("web.Dialog"); |
|||
var core = require('web.core'); |
|||
var qweb = core.qweb; |
|||
var _t = core._t; |
|||
|
|||
var JoinNodeDialog = Dialog.extend({ |
|||
xmlDependencies: Dialog.prototype.xmlDependencies.concat([ |
|||
'/bi_view_editor/static/src/xml/bi_view_editor.xml' |
|||
]), |
|||
events: { |
|||
"click li": "choiceClicked", |
|||
}, |
|||
init: function(parent, options, choices, model_data) { |
|||
this.choices = choices; |
|||
// Prepare data for view
|
|||
for (var i = 0; i < choices.length; i++) { |
|||
if (choices[i].join_node !== -1 && choices[i].table_alias !== -1) { |
|||
choices[i].model_name = model_data[choices[i].table_alias].model_name; |
|||
} |
|||
choices[i].index = i; |
|||
} |
|||
|
|||
var defaults = _.defaults(options || {}, { |
|||
title: _t("Join..."), |
|||
dialogClass: 'oe_act_window', |
|||
$content: qweb.render('bi_view_editor.JoinNodeDialog', { |
|||
'choices': choices |
|||
}), |
|||
buttons: [{ |
|||
text: _t("Cancel"), |
|||
classes: "btn-default o_form_button_cancel", |
|||
close: true |
|||
}], |
|||
}); |
|||
this._super(parent, defaults); |
|||
}, |
|||
choiceClicked: function(e) { |
|||
this.trigger('chosen', { |
|||
choice: this.choices[$(e.currentTarget).attr('data-index')] |
|||
}); |
|||
this.close(); |
|||
} |
|||
}); |
|||
|
|||
return JoinNodeDialog; |
|||
}); |
@ -0,0 +1,171 @@ |
|||
/* Copyright 2015-2018 Onestein (<http://www.onestein.eu>) |
|||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
|||
|
|||
odoo.define('bi_view_editor.ModelList', function (require) { |
|||
"use strict"; |
|||
|
|||
var Widget = require('web.Widget'); |
|||
var core = require('web.core'); |
|||
var session = require('web.session'); |
|||
var qweb = core.qweb; |
|||
|
|||
var ModelList = Widget.extend({ |
|||
template: 'bi_view_editor.ModelList', |
|||
events: { |
|||
'keyup .search-bar > input': 'filterChanged' |
|||
}, |
|||
init: function(parent) { |
|||
var res = this._super(parent); |
|||
this.active_models = []; |
|||
this.cache_fields = {}; |
|||
this.current_filter = ''; |
|||
this.mode = null; |
|||
return res; |
|||
}, |
|||
setMode: function(mode) { |
|||
if(mode === 'readonly') { |
|||
this.$el.find('.search-bar').attr('disabled', true); |
|||
this.$el.find('.class-list, .class').addClass('readonly'); |
|||
} else { |
|||
this.$el.find('.search-bar').attr('disabled', false); |
|||
this.$el.find('.class-list, .class').removeClass('readonly'); |
|||
} |
|||
this.mode = mode; |
|||
}, |
|||
isActive: function(id) { |
|||
return this.active_models.indexOf(id) !== -1; |
|||
}, |
|||
removeAsActive: function(id) { |
|||
var i = this.active_models.indexOf(id); |
|||
this.active_models.splice(i, 1); |
|||
}, |
|||
addAsActive: function(id) { |
|||
this.active_models.push(id); |
|||
}, |
|||
loadModels: function(model_ids) { |
|||
if (model_ids) { |
|||
return this._rpc({ |
|||
model: 'ir.model', |
|||
method: 'get_related_models', |
|||
args: [model_ids], |
|||
context: { |
|||
lang: session.user_context.lang |
|||
} |
|||
}); |
|||
} |
|||
return this._rpc({ |
|||
model: 'ir.model', |
|||
method: 'get_models', |
|||
context: { |
|||
lang: session.user_context.lang |
|||
} |
|||
}); |
|||
}, |
|||
loadFields: function(model_id) { |
|||
if (!(model_id in this.cache_fields)) { |
|||
var deferred = this._rpc({ |
|||
model: 'ir.model', |
|||
method: 'get_fields', |
|||
args: [model_id], |
|||
context: { |
|||
lang: session.user_context.lang |
|||
} |
|||
}); |
|||
this.cache_fields[model_id] = deferred; |
|||
} |
|||
return this.cache_fields[model_id]; |
|||
}, |
|||
populateModels: function (models) { |
|||
var self = this; |
|||
this.$el.find(".class-list").html(''); |
|||
|
|||
_.each(models, function(model) { |
|||
var $html = $(qweb.render('bi_view_editor.ModelListItem', { |
|||
'id': model.id, |
|||
'model': model.model, |
|||
'name': model.name |
|||
})); |
|||
$html.find('.class').data('model', model).click(function() { |
|||
self.modelClicked($(this)); |
|||
}); |
|||
self.$el.find(".class-list").append($html); |
|||
|
|||
if (self.isActive(model.id)) { |
|||
self.loadFields(model.id).done(function(fields) { |
|||
self.populateFields(fields, model.id); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
populateFields: function(fields, model_id) { |
|||
var self = this; |
|||
if(!model_id && fields.length === 0) { |
|||
return; |
|||
} |
|||
var data_model_id = model_id; |
|||
if(!data_model_id) { |
|||
data_model_id = fields[0].model_id; |
|||
} |
|||
var $model_item = this.$el.find(".class[data-id='" + data_model_id + "']"); |
|||
_.each(fields, function(field) { |
|||
var $field = $(qweb.render('bi_view_editor.ModelListFieldItem', { |
|||
name: field.name, |
|||
description: field.description |
|||
})).data('field', field).click(function() { |
|||
self.fieldClicked($(this)); |
|||
}).draggable({ |
|||
'revert': 'invalid', |
|||
'scroll': false, |
|||
'helper': 'clone', |
|||
'appendTo': 'body', |
|||
'containment': 'window' |
|||
}); |
|||
$model_item.after($field); |
|||
|
|||
}); |
|||
}, |
|||
modelClicked: function($el) { |
|||
if (this.mode === 'readonly') { |
|||
return; |
|||
} |
|||
var model = $el.data('model'); |
|||
$el.parent().find('.field').remove(); |
|||
if(this.isActive(model.id)) { |
|||
this.removeAsActive(model.id); |
|||
} else { |
|||
this.addAsActive(model.id); |
|||
this.loadFields(model.id).done(function(fields) { |
|||
this.populateFields(fields, model.id); |
|||
}.bind(this)); |
|||
} |
|||
}, |
|||
fieldClicked: function($el) { |
|||
if (this.mode === 'readonly') { |
|||
return; |
|||
} |
|||
this.trigger('field_clicked', $el.data('field')); |
|||
}, |
|||
filterChanged: function(e) { |
|||
var $input = $(e.target); |
|||
this.filter($input.val()); |
|||
}, |
|||
filter: function(value) { |
|||
this.active_models = []; |
|||
this.$el.find('.field').remove(); |
|||
var val = typeof value === 'undefined' ? this.current_filter : value.toLowerCase(); |
|||
this.$el.find(".class").each(function() { |
|||
var data = $(this).data('model'); |
|||
if (data.name.toLowerCase().indexOf(val) === -1 && |
|||
data.model.toLowerCase().indexOf(val) === -1) { |
|||
$(this).addClass('hidden'); |
|||
} else { |
|||
$(this).removeClass('hidden'); |
|||
} |
|||
}); |
|||
this.current_filter = val; |
|||
} |
|||
}); |
|||
|
|||
return ModelList; |
|||
|
|||
}); |
@ -0,0 +1,143 @@ |
|||
/* Copyright 2015-2018 Onestein (<http://www.onestein.eu>) |
|||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
|||
|
|||
odoo.define('bi_view_editor', function (require) { |
|||
"use strict"; |
|||
|
|||
var JoinNodeDialog = require('bi_view_editor.JoinNodeDialog'); |
|||
var ModelList = require('bi_view_editor.ModelList'); |
|||
var FieldList = require('bi_view_editor.FieldList').FieldList; |
|||
|
|||
var AbstractField = require('web.AbstractField'); |
|||
var Data = require('web.data'); |
|||
var field_registry = require('web.field_registry'); |
|||
|
|||
var BiViewEditor = AbstractField.extend({ |
|||
template: "bi_view_editor.Frame", |
|||
events: { |
|||
"click .clear-btn": "clear" |
|||
}, |
|||
start: function() { |
|||
var self = this; |
|||
var res = this._super.apply(this, arguments); |
|||
|
|||
// Init ModelList
|
|||
this.model_list = new ModelList(this); |
|||
this.model_list.appendTo(this.$(".body > .left")); |
|||
this.model_list.on('field_clicked', this, function(field) { |
|||
self.addField(_.extend({}, field)); |
|||
}); |
|||
|
|||
// Init FieldList
|
|||
this.field_list = new FieldList(this); |
|||
this.field_list.appendTo(this.$(".body > .right")); |
|||
this.field_list.on('removed', this, this.fieldListRemoved); |
|||
this.field_list.on('updated', this, this.fieldListChanged); |
|||
|
|||
this.$el.find(".body > .right").droppable({ |
|||
accept: "div.class-list div.field", |
|||
drop: function (event, ui) { |
|||
self.addField(_.extend({}, ui.draggable.data('field'))); |
|||
ui.draggable.draggable('option', 'revert', false); |
|||
} |
|||
}); |
|||
|
|||
this.on("change:effective_readonly", this, function() { |
|||
this.updateMode(); |
|||
}); |
|||
this.renderValue(); |
|||
this.loadAndPopulateModelList(); |
|||
this.updateMode(); |
|||
return res; |
|||
}, |
|||
clear: function() { |
|||
if (this.mode !== 'readonly') { |
|||
this.field_list.set([]); |
|||
this.loadAndPopulateModelList(); |
|||
this._setValue(JSON.stringify(this.field_list.get())); |
|||
} |
|||
}, |
|||
fieldListChanged: function() { |
|||
this._setValue(JSON.stringify(this.field_list.get())); |
|||
}, |
|||
fieldListRemoved: function() { |
|||
this.loadAndPopulateModelList(); |
|||
this._setValue(JSON.stringify(this.field_list.get())); |
|||
}, |
|||
renderValue: function() { |
|||
this.field_list.set(JSON.parse(this.value)); |
|||
}, |
|||
updateMode: function () { |
|||
if (this.mode === 'readonly') { |
|||
this.$el.find('.clear-btn').addClass('hidden'); |
|||
this.$el.find(".body .right").droppable("option", "disabled", true); |
|||
} else { |
|||
this.$el.find('.clear-btn').removeClass('hidden'); |
|||
this.$el.find('.body .right').droppable('option', 'disabled', false); |
|||
} |
|||
this.field_list.setMode(this.mode); |
|||
this.model_list.setMode(this.mode); |
|||
}, |
|||
loadAndPopulateModelList: function() { |
|||
var model_ids = null; |
|||
if (this.field_list.get().length > 0) { |
|||
model_ids = this.field_list.getModelIds(); |
|||
} |
|||
this.model_list.loadModels(model_ids).done(function(models) { |
|||
this.model_list.populateModels(models); |
|||
}.bind(this)); |
|||
}, |
|||
getTableAlias: function(field) { |
|||
if (typeof field.table_alias === 'undefined') { |
|||
var model_ids = this.field_list.getModelIds(); |
|||
var n = 0; |
|||
while (typeof model_ids["t" + n] !== 'undefined') { |
|||
n++; |
|||
} |
|||
return "t" + n; |
|||
} |
|||
return field.table_alias; |
|||
}, |
|||
addFieldAndJoinNode: function(field, join_node) { |
|||
if (join_node.join_node === -1 || join_node.table_alias === -1) { |
|||
field.table_alias = this.getTableAlias(field); |
|||
if (join_node.join_node === -1) { |
|||
join_node.join_node = field.table_alias; |
|||
} else { |
|||
join_node.table_alias = field.table_alias; |
|||
} |
|||
this.field_list.add(join_node); |
|||
} else { |
|||
field.table_alias = join_node.table_alias; |
|||
} |
|||
|
|||
this.field_list.add(field); |
|||
this.loadAndPopulateModelList(); |
|||
this._setValue(JSON.stringify(this.field_list.get())); |
|||
}, |
|||
addField: function(field) { |
|||
var data = _.extend({}, field); |
|||
var model = new Data.DataSet(this, "ir.model"); |
|||
var field_data = this.field_list.get(); |
|||
model.call('get_join_nodes', [field_data, data]).then(function(result) { |
|||
if (result.length === 1) { |
|||
this.addFieldAndJoinNode(data, result[0]); |
|||
} else if (result.length > 1) { |
|||
var dialog = new JoinNodeDialog(this, {}, result, this.field_list.getModelData()); |
|||
dialog.open().on('chosen', this, function(e) { |
|||
this.addFieldAndJoinNode(data, e.choice); |
|||
}); |
|||
} else { |
|||
var table_alias = this.getTableAlias(data); |
|||
data.table_alias = table_alias; |
|||
this.field_list.add(data); |
|||
this.loadAndPopulateModelList(); |
|||
this._setValue(JSON.stringify(this.field_list.get())); |
|||
} |
|||
}.bind(this)); |
|||
} |
|||
}); |
|||
|
|||
field_registry.add('BVEEditor', BiViewEditor); |
|||
|
|||
}); |
@ -1,445 +0,0 @@ |
|||
odoo.define('bi_view_editor', function (require) { |
|||
"use strict"; |
|||
|
|||
var Core = require("web.core"); |
|||
var FormCommon = require('web.form_common'); |
|||
var Model = require('web.Model'); |
|||
var Data = require('web.data'); |
|||
var Widget = require('web.Widget'); |
|||
var Dialog = require("web.Dialog"); |
|||
var _t = Core._t; |
|||
|
|||
var JoinNodePopup = Widget.extend({ |
|||
template: "JoinNodePopup", |
|||
start: function() { |
|||
var self = this; |
|||
}, |
|||
|
|||
display_popup: function(choices, model_data, callback, callback_data) { |
|||
var self = this; |
|||
this.renderElement(); |
|||
var joinnodes = this.$el.find('#join-nodes'); |
|||
joinnodes.empty(); |
|||
for (var i=0; i<choices.length; i++) { |
|||
var description = ""; |
|||
if (choices[i].join_node !== -1 && choices[i].table_alias !== -1) { |
|||
description = _t("Use the field on model") + " <b>" + model_data[choices[i].table_alias].model_name + "</b>"; |
|||
} else { |
|||
var new_str = ""; |
|||
if (choices[i].join_node !== -1) { |
|||
new_str = "<b>" + _t("new") + "</b> "; |
|||
} |
|||
description = _t("<b>Join</b> using the field") + " <u><b>" + choices[i].description + "</b></u> " + _t("on ") + new_str + _t("model") +" <b>" + choices[i].model_name + "</b>"; |
|||
} |
|||
joinnodes.append($('<a><input type="radio">' + description+ '</a>') |
|||
.data('idx', i) |
|||
.wrap('<p></p>') |
|||
.parent()); |
|||
} |
|||
var dialog = new Dialog(this, { |
|||
dialogClass: 'oe_act_window', |
|||
title: _t("Choose join node"), |
|||
$content: this.$el, |
|||
buttons: [{text: _t("Cancel"), |
|||
classes: "btn-default o_form_button_cancel", |
|||
close: true |
|||
}] |
|||
}).open(); |
|||
|
|||
joinnodes.find('a').click(function() { |
|||
callback(callback_data, choices[$(this).data('idx')]); |
|||
dialog.close(); |
|||
}); |
|||
|
|||
this.start(); |
|||
} |
|||
}); |
|||
|
|||
var BiViewEditor = FormCommon.AbstractField.extend({ |
|||
template: "BVEEditor", |
|||
activeModelMenus: [], |
|||
currentFilter: "", |
|||
init: function() { |
|||
this._super.apply(this, arguments); |
|||
}, |
|||
start: function() { |
|||
this._super(); |
|||
this.on("change:effective_readonly", this, function() { |
|||
this.display_field(); |
|||
this.render_value(); |
|||
}); |
|||
this.display_field(); |
|||
this.render_value(); |
|||
}, |
|||
display_field: function () { |
|||
var self = this; |
|||
this.$el.find(".body .right").droppable({ |
|||
accept: "div.class-list div.field", |
|||
drop: function (event, ui) { |
|||
self.add_field(ui.draggable); |
|||
ui.draggable.draggable('option', 'revert', false ); |
|||
} |
|||
}); |
|||
if (!this.get("effective_readonly")) { |
|||
this.$el.find('.search-bar').attr('disabled', false); |
|||
this.$el.find('.class-list').css('opacity', '1'); |
|||
this.$el.find('.class-list .class').css('cursor', 'pointer'); |
|||
this.$el.find(".body .right").droppable("option", "disabled", false); |
|||
this.$el.find('#clear').css('display', 'inline-block').click(function () { |
|||
self.set_fields([]); |
|||
self.internal_set_value('[]'); |
|||
}); |
|||
this.$el.find('.search-bar input').keyup(function(e) { |
|||
//Local filter
|
|||
self.filter($(this).val()); |
|||
}); |
|||
} else { |
|||
this.$el.find(".body .right").droppable("option", "disabled", true); |
|||
this.$el.find('#clear').css('display', 'none'); |
|||
this.$el.find('.search-bar').attr('disabled', true); |
|||
this.$el.find('.class-list').css('opacity', '.35'); |
|||
this.$el.find('.class-list .class').css('cursor', 'default'); |
|||
} |
|||
}, |
|||
filter: function(val) { |
|||
val = (typeof val !== 'undefined') ? val.toLowerCase() : this.currentFilter; |
|||
this.currentFilter = val; |
|||
this.$el.find(".class-list .class-container").each(function() { |
|||
var modelData = $(this).find(".class").data('model-data'); |
|||
//TODO: filter on all model fields (name, technical name, etc)
|
|||
|
|||
if(typeof modelData === 'undefined' || (modelData.name.toLowerCase().indexOf(val) === -1 && modelData.model.toLowerCase().indexOf(val) === -1)) |
|||
$(this).hide(); |
|||
else |
|||
$(this).show(); |
|||
}); |
|||
}, |
|||
get_field_icons: function(field) { |
|||
var icons = ""; |
|||
if(field.column) |
|||
icons += "<span class='fa fa-columns' title='Column'></span> "; |
|||
if(field.row) |
|||
icons += "<span class='fa fa-bars' title='Row'></span> "; |
|||
if(field.measure) |
|||
icons += "<span class='fa fa-bar-chart-o' title='Measure'></span> "; |
|||
if(field.list) |
|||
icons += "<span class='fa fa-list' title='List'></span> "; |
|||
|
|||
return icons; |
|||
}, |
|||
update_field_view: function(row) { |
|||
row.find("td:nth-child(3)").html(this.get_field_icons(row.data('field-data'))); |
|||
}, |
|||
render_value: function() { |
|||
this.set_fields(JSON.parse(this.get('value'))); |
|||
}, |
|||
load_classes: function(scrollTo) { |
|||
scrollTo = (typeof scrollTo === 'undefined') ? false : scrollTo; |
|||
var self = this; |
|||
var model = new Model("ir.model"); |
|||
if (this.$el.find(".field-list tbody tr").length > 0) { |
|||
model.call("get_related_models", [this.get_model_ids()], { context: new Data.CompoundContext() }).then(function(result) { |
|||
self.show_classes(result); |
|||
}); |
|||
} else { |
|||
model.call("get_models", { context: new Data.CompoundContext() }).then(function(result) { |
|||
self.show_classes(result); |
|||
}); |
|||
} |
|||
}, |
|||
show_classes: function (result) { |
|||
var self = this; |
|||
var model = new Model("ir.model"); |
|||
self.$el.find(".class-list .class").remove(); |
|||
self.$el.find(".class-list .field").remove(); |
|||
var css = this.get('effective_readonly') ? 'cursor: default' : 'cursor: pointer'; |
|||
function addField() { |
|||
if (!self.get("effective_readonly")) { |
|||
self.add_field($(this)); |
|||
} |
|||
} |
|||
function clickHandler(evt) { |
|||
if(self.get("effective_readonly")) return; |
|||
var classel = $(this); |
|||
if (classel.data('bve-processed')) { |
|||
classel.parent().find('.field').remove(); |
|||
classel.data('bve-processed', false); |
|||
var index = self.activeModelMenus.indexOf(classel.data('model-data').id); |
|||
if(index !== -1) self.activeModelMenus.splice(index, 1); |
|||
} else { |
|||
self.activeModelMenus.push(classel.data('model-data').id); |
|||
model.call("get_fields", [classel.data('model-data').id], { context: new Data.CompoundContext() }).then(function(result) { |
|||
for (var i = 0; i < result.length; i++) { |
|||
classel.find("#bve-field-" + result[i].name).remove(); |
|||
self._render_field(self, i, result, classel, addField) |
|||
} |
|||
}); |
|||
$(this).data('bve-processed', true); |
|||
} |
|||
} |
|||
function renderFields(result) { |
|||
if (typeof(result[0]) !== 'undefined') { |
|||
var item = self.$el.find(".class-list #bve-class-" + result[0].model_id); |
|||
for (var o = 0; o < result.length; o++) { |
|||
self._render_field(self, o, result, item, addField) |
|||
} |
|||
item.data('bve-processed', true); |
|||
} |
|||
} |
|||
for (var i = 0; i < result.length; i++) { |
|||
var item = $("<div style=\"" + css + "\" class=\"class\" title=\"" + result[i].model + "\" id=\"bve-class-" + result[i].id + "\">" + result[i].name + "</div>") |
|||
.data('model-data', result[i]) |
|||
.click(clickHandler) |
|||
.wrap("<div class=\"class-container\"></div>").parent(); |
|||
self.$el.find(".class-list").append(item); |
|||
|
|||
var index = self.activeModelMenus.indexOf(item.find(".class").data('model-data').id); |
|||
if(index !== -1 && !self.get("effective_readonly")) { |
|||
model.call("get_fields", [self.activeModelMenus[index]], { context: new Data.CompoundContext() }).then(renderFields); |
|||
} |
|||
self.filter(); |
|||
} |
|||
|
|||
}, |
|||
_render_field(_self, _index, _result, _item, _addField) { |
|||
if(_self.$el.find(".field-list tbody [name=label-" + _result[_index].id + "]").length > 0) return; |
|||
_item.after($("<div class=\"field\" title=\"" + _result[_index].name + "\" id=\"bve-field-" + _result[_index].name + "\">" + _result[_index].description + "</div>") |
|||
.data('field-data', _result[_index]) |
|||
.click(_addField) |
|||
.draggable({ |
|||
'revert': 'invalid', |
|||
'scroll': false, |
|||
'helper': 'clone', |
|||
'appendTo': 'body', |
|||
'containment': 'window' |
|||
}) |
|||
); |
|||
}, |
|||
set_checkbox: function(check, identifier, _contextMenu) { |
|||
if(check) |
|||
_contextMenu.find(identifier).attr('checked', true); |
|||
else |
|||
_contextMenu.find(identifier).attr('checked', false); |
|||
}, |
|||
_false_if_undefined: function(to_check) { |
|||
if (typeof to_check === 'undefined') return false; |
|||
return to_check; |
|||
}, |
|||
_true_if_undefined: function(to_check) { |
|||
if (typeof to_check === 'undefined') return true; |
|||
return to_check; |
|||
}, |
|||
add_field_to_table: function(data, options) { |
|||
var self = this; |
|||
data.row = self._false_if_undefined(data.row); |
|||
data.column = self._false_if_undefined(data.column); |
|||
data.measure = self._false_if_undefined(data.measure); |
|||
data.list = self._true_if_undefined(data.list); |
|||
|
|||
var n = 1; |
|||
var name = data.name; |
|||
function checkNameMatches(el) { return el.name === data.name;} |
|||
while ($.grep(self.get_fields(), checkNameMatches).length > 0) { |
|||
data.name = name + '_' + n; |
|||
n += 1; |
|||
} |
|||
var classes = ""; |
|||
if (typeof data.join_node !== 'undefined') { |
|||
classes = "join-node displaynone"; |
|||
} |
|||
var delete_button = ""; |
|||
var disabled = " disabled=\"disabled\" "; |
|||
if (!this.get("effective_readonly")) { |
|||
delete_button = "<span id=\"delete-" + data.id + "\" class=\"delete-button fa fa-trash-o\"/>"; |
|||
disabled = ""; |
|||
} |
|||
|
|||
self.$el.find(".field-list tbody") |
|||
.append($("<tr class=\"" + classes + "\"><td><input " + disabled + "title=\"" + data.name + " (" + data.model + ")\" type=\"text\" name=\"label-" + data.id + "\" value=\"" + data.description + "\"/></td><td>" + data.model_name + "</td><td>" + self.get_field_icons(data) + "</td><td>" + delete_button + "</td></tr>") |
|||
.data('field-data', data) |
|||
.contextmenu(function(e) { |
|||
e.preventDefault(); |
|||
if (self.get("effective_readonly")) return; |
|||
var target = $(e.currentTarget); |
|||
var currentFieldData = target.data('field-data'); |
|||
|
|||
var contextMenu = self.$el.find(".context-menu"); |
|||
contextMenu.css("left", e.pageX + "px"); |
|||
contextMenu.css("top", e.pageY + "px"); |
|||
contextMenu.mouseleave(function() { |
|||
contextMenu.hide(); |
|||
}); |
|||
contextMenu.find("li").hover(function() { |
|||
$(this).find("ul").css("color", "#000"); |
|||
$(this).find("ul").show(); |
|||
}, function() { |
|||
$(this).find("ul").hide(); |
|||
}); |
|||
|
|||
|
|||
//Set checkboxes
|
|||
self.set_checkbox(currentFieldData.column, '#column-checkbox', contextMenu); |
|||
self.set_checkbox(currentFieldData.row, '#row-checkbox', contextMenu); |
|||
self.set_checkbox(currentFieldData.measure, '#measure-checkbox', contextMenu); |
|||
self.set_checkbox(currentFieldData.list, '#list-checkbox', contextMenu); |
|||
|
|||
var to_disable = false; |
|||
if(currentFieldData.type === "float" || currentFieldData.type === "integer" || currentFieldData.type === "monetary") to_disable = true; |
|||
var identifiers = [['#column-checkbox', 'column', to_disable], ['#row-checkbox', 'row', to_disable], ['#measure-checkbox', 'measure', !to_disable], ['#list-checkbox', 'list', false]]; |
|||
identifiers.forEach(function (element) { |
|||
contextMenu.find(element[0]).attr('disabled', element[2]); |
|||
}); |
|||
|
|||
//Add change events
|
|||
identifiers.forEach(function (element) { |
|||
contextMenu.find(element[0]).unbind("change"); |
|||
contextMenu.find(element[0]).change(function() { |
|||
currentFieldData[element[1]] = $(this).is(":checked"); |
|||
target.data('field-data', currentFieldData); |
|||
self.update_field_view(target); |
|||
self.internal_set_value(JSON.stringify(self.get_fields())); |
|||
}); |
|||
|
|||
}); |
|||
contextMenu.show(); |
|||
|
|||
$(document).mouseup(function (e) { |
|||
var container = $(".context-menu"); |
|||
|
|||
// if the target of the click isn't the container nor a descendant of the container
|
|||
if (!container.is(e.target) && container.has(e.target).length === 0) |
|||
{ |
|||
container.hide(); |
|||
} |
|||
}); |
|||
|
|||
}) |
|||
); |
|||
|
|||
self.$el.find('.delete-button').unbind("click"); |
|||
self.$el.find('.delete-button').click(function() { |
|||
$(this).closest('tr').remove(); |
|||
self.clean_join_nodes(); |
|||
self.internal_set_value(JSON.stringify(self.get_fields())); |
|||
self.load_classes(); |
|||
self.$el.find(".field-list .delete-button").hide(); |
|||
self.$el.find(".field-list .delete-button:last").show(); |
|||
return false; |
|||
}); |
|||
|
|||
self.$el.find(".field-list .delete-button").hide(); |
|||
self.$el.find(".field-list .delete-button:last").show(); |
|||
}, |
|||
clean_join_nodes: function () { |
|||
var aliases = $.makeArray(this.$el.find(".field-list tbody tr").map(function (idx, el) { |
|||
var d = $(this).data('field-data'); |
|||
return d.table_alias; |
|||
})); |
|||
|
|||
this.$el.find(".field-list tbody tr").each(function (idx, el) { |
|||
var d = $(this).data('field-data'); |
|||
if (typeof d.join_node !== 'undefined' && aliases.indexOf(d.join_node) === -1) { |
|||
$(this).remove(); |
|||
} |
|||
}); |
|||
}, |
|||
get_model_ids: function () { |
|||
var model_ids = {}; |
|||
this.$el.find(".field-list tbody tr").each(function (idx, el) { |
|||
var d = $(this).data('field-data'); |
|||
model_ids[d.table_alias] = d.model_id; |
|||
}); |
|||
return model_ids; |
|||
}, |
|||
get_model_data: function () { |
|||
var model_data = {}; |
|||
this.$el.find(".field-list tbody tr").each(function (idx, el) { |
|||
var d = $(this).data('field-data'); |
|||
model_data[d.table_alias] = {model_id: d.model_id, model_name: d.model_name}; |
|||
}); |
|||
return model_data; |
|||
}, |
|||
get_table_alias: function(field) { |
|||
if (typeof field.table_alias !== 'undefined') { |
|||
return field.table_alias; |
|||
} else { |
|||
var model_ids = this.get_model_ids(); |
|||
var n = 0; |
|||
while (typeof model_ids["t" + n] !== 'undefined') n++; |
|||
return "t" + n; |
|||
} |
|||
}, |
|||
add_field_and_join_node: function(field, join_node) { |
|||
var self = this; |
|||
|
|||
var go_to_else = true; |
|||
if (join_node.join_node === -1 || join_node.table_alias === -1){ |
|||
go_to_else = false; |
|||
field.table_alias = self.get_table_alias(field); |
|||
if (join_node.join_node === -1) join_node.join_node = field.table_alias; |
|||
else join_node.table_alias = field.table_alias; |
|||
self.add_field_to_table(join_node); |
|||
} |
|||
else field.table_alias = join_node.table_alias; |
|||
|
|||
self.add_field_to_table(field); |
|||
self.internal_set_value(JSON.stringify(self.get_fields())); |
|||
self.load_classes(field); |
|||
}, |
|||
add_field: function(field) { |
|||
var self = this; |
|||
|
|||
// Quick fix for double click
|
|||
if(self._adding) { |
|||
return; |
|||
} |
|||
self._adding = true; |
|||
setTimeout(function() { |
|||
self._adding = false; |
|||
}, 1000); |
|||
// End quick fix
|
|||
|
|||
var data = field.data('field-data'); |
|||
var model = new Model("ir.model"); |
|||
var model_ids = this.get_model_ids(); |
|||
var field_data = this.get_fields(); |
|||
model.call('get_join_nodes', [field_data, data], {context: new Data.CompoundContext()}).then(function(result) { |
|||
|
|||
if (result.length === 1) { |
|||
self.add_field_and_join_node(data, result[0]); |
|||
self.internal_set_value(JSON.stringify(self.get_fields())); |
|||
} else if (result.length > 1) { |
|||
var pop = new JoinNodePopup(self); |
|||
pop.display_popup(result, self.get_model_data(), self.add_field_and_join_node.bind(self), data); |
|||
} else { |
|||
// first field and table only.
|
|||
var table_alias = self.get_table_alias(data); |
|||
data.table_alias = table_alias; |
|||
self.add_field_to_table(data); |
|||
self.internal_set_value(JSON.stringify(self.get_fields())); |
|||
self.load_classes(data); |
|||
} |
|||
}); |
|||
}, |
|||
get_fields: function() { |
|||
return $.makeArray(this.$el.find(".field-list tbody tr").map(function (idx, el) { |
|||
var d = $(this).data('field-data'); |
|||
d.description = $("input[name='label-" + d.id + "']").val(); |
|||
return d; |
|||
})); |
|||
}, |
|||
set_fields: function(values) { |
|||
this.activeModelMenus = []; |
|||
if (!values) { |
|||
values = []; |
|||
} |
|||
this.$el.find('.field-list tbody tr').remove(); |
|||
for(var i = 0; i < values.length; i++) { |
|||
this.add_field_to_table(values[i]); |
|||
} |
|||
this.load_classes(); |
|||
} |
|||
}); |
|||
Core.form_widget_registry.add('BVEEditor', BiViewEditor); |
|||
|
|||
}); |
@ -0,0 +1,183 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<template> |
|||
|
|||
<t t-name="bi_view_editor.Frame"> |
|||
<div class="oe_form_field_bi_editor"> |
|||
|
|||
<div class="body"> |
|||
|
|||
<div class="left"> |
|||
</div> |
|||
<div class="right"> |
|||
|
|||
</div> |
|||
<div class="clear"></div> |
|||
</div> |
|||
<div class="footer"> |
|||
<div class="left"></div> |
|||
<div class="right"><button class="clear-btn hidden"><span class="fa fa-eraser"></span> Clear</button></div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<!-- Join Node Dialog --> |
|||
<t t-name="bi_view_editor.JoinNodeDialog"> |
|||
<div class="oe_bi_view_editor_join_node_dialog"> |
|||
<ul class="list-group" > |
|||
<t t-foreach="choices" t-as="choice"> |
|||
<t t-if="choice.join_node !== -1 and choice.table_alias !== -1"> |
|||
<li class="list-group-item list-group-item-action text-primary" t-attf-data-index="#{choice.index}"> |
|||
<b>Use the existing node</b> |
|||
</li> |
|||
</t> |
|||
<t t-elif="choice.join_node !== -1"> |
|||
<li class="list-group-item list-group-item-action text-success" t-attf-data-index="#{choice.index}"> |
|||
Use the field |
|||
<b><t t-esc="choice.model_name"/></b> |
|||
<i class="fa fa-caret-right"/> |
|||
<b><t t-esc="choice.description"/></b> |
|||
<span class="badge">new</span> |
|||
</li> |
|||
</t> |
|||
<t t-else=""> |
|||
<li class="list-group-item list-group-item-action" t-attf-data-index="#{choice.index}"> |
|||
Use the field |
|||
<b><t t-esc="choice.model_name"/></b> |
|||
<i class="fa fa-caret-right"/> |
|||
<b><t t-esc="choice.description"/></b> |
|||
</li> |
|||
</t> |
|||
</t> |
|||
</ul> |
|||
</div> |
|||
</t> |
|||
|
|||
<!-- ModelList --> |
|||
<t t-name="bi_view_editor.ModelList"> |
|||
<div> |
|||
<div class="search-bar"> |
|||
<span class="fa fa-search"></span> |
|||
<input type="text" class="search-bar" /> |
|||
</div> |
|||
<div class="class-list"> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<!-- ModelListItem --> |
|||
<t t-name="bi_view_editor.ModelListItem"> |
|||
<div class="class-container"> |
|||
<div class="class" t-attf-title="#{model}" t-attf-data-id="#{id}"><t t-esc="name"/></div> |
|||
</div> |
|||
</t> |
|||
|
|||
<!-- ModelListFieldItem--> |
|||
<t t-name="bi_view_editor.ModelListFieldItem"> |
|||
<div class="field" t-attf-title="#{name}" t-attf-data-id="#{name}"><t t-esc="description"/></div> |
|||
</t> |
|||
|
|||
<!-- FieldList --> |
|||
<t t-name="bi_view_editor.FieldList"> |
|||
<div> |
|||
<table class="field-list"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Model</th> |
|||
<th>Options</th> |
|||
<th></th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</t> |
|||
|
|||
<!-- FieldContextMenu --> |
|||
<t t-name="bi_view_editor.FieldList.FieldContextMenu"> |
|||
<ul class="context-menu hidden"> |
|||
<li> |
|||
<div class="checkbox"> |
|||
<label> |
|||
<input type="checkbox" data-for="column" class="checkbox-column"/> Column |
|||
</label> |
|||
</div> |
|||
</li> |
|||
<li> |
|||
<div class="checkbox"> |
|||
<label> |
|||
<input type="checkbox" data-for="row" class="checkbox-row"/> Row |
|||
</label> |
|||
</div> |
|||
</li> |
|||
<li> |
|||
<div class="checkbox"> |
|||
<label> |
|||
<input type="checkbox" data-for="measure" class="checkbox-measure"/> Measure |
|||
</label> |
|||
</div> |
|||
</li> |
|||
<li> |
|||
<div class="checkbox"> |
|||
<label> |
|||
<input type="checkbox" data-for="list" class="checkbox-list"/> List |
|||
</label> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
</t> |
|||
|
|||
<!-- JoinContextMenu --> |
|||
<t t-name="bi_view_editor.FieldList.JoinContextMenu"> |
|||
<ul class="context-menu hidden"> |
|||
<li> |
|||
<div class="checkbox"> |
|||
<label> |
|||
<input type="checkbox" data-for="join_left" class="checkbox-join-left"/> Join Left |
|||
</label> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
</t> |
|||
|
|||
<!-- FieldListItem --> |
|||
<t t-name="bi_view_editor.FieldListItem"> |
|||
<tr t-attf-data-id="#{field._id}" class="field-node"> |
|||
<td> |
|||
<input t-attf-data-id="#{field._id}" t-attf-title="#{field.model_name} (#{field.model})" class="form-control input-sm" type="text" name="description" t-attf-value="#{field.description}"/> |
|||
</td> |
|||
<td> |
|||
<t t-esc="field.model_name" /> |
|||
</td> |
|||
<td> |
|||
<span data-for="column" t-attf-class="#{field.column and 'fa fa-columns' or 'fa fa-columns hidden'}" title='Column'></span> |
|||
<span data-for="row" t-attf-class="#{field.row and 'fa fa-bars' or 'fa fa-bars hidden'}" title='Row'></span> |
|||
<span data-for="measure" t-attf-class="#{field.measure and 'fa fa-bar-chart-o' or 'fa fa-bar-chart-o hidden'}" title='Measure'></span> |
|||
<span data-for="list" t-attf-class="#{field.list and 'fa fa-list' or 'fa fa-list hidden'}" title='List'></span> |
|||
</td> |
|||
<td> |
|||
<span t-attf-data-id="#{field._id}" class="delete-button fa fa-trash-o"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
|
|||
<t t-name="bi_view_editor.JoinListItem"> |
|||
<tr t-attf-data-id="#{field._id}" class="join-node"> |
|||
<td colspan="4"> |
|||
<input class="hidden" type="text" name="description" t-attf-value="#{field.description}"/> |
|||
<t t-if="field.join_node > field.table_alias"> |
|||
<b><t t-esc="field.model_name" /></b> |
|||
<i class="fa fa-caret-right"/> |
|||
<small><t t-esc="field.description" /></small> |
|||
</t> |
|||
<t t-else=""> |
|||
<small><t t-esc="field.description" /></small> |
|||
<i class="fa fa-caret-left"/> |
|||
<b><t t-esc="field.model_name" /></b> |
|||
</t> |
|||
<span t-attf-class="#{!field.join_left and 'hidden' or ''}" data-for="join_left"><i>(join left)</i></span> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</template> |
@ -1,54 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates id="template" xml:space="preserve"> |
|||
<t t-name="JoinNodePopup"> |
|||
<div class="oe_form_nosheet"> |
|||
<div id="join-nodes"> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
|
|||
<t t-name="BVEEditor"> |
|||
<div class="oe_form_field_bi_editor"> |
|||
|
|||
<div class="body"> |
|||
|
|||
<div class="left"> |
|||
<div class="search-bar"> |
|||
<span class="fa fa-search"></span> |
|||
<input type="text" class="search-bar" /> |
|||
</div> |
|||
<div class="class-list"> |
|||
</div> |
|||
</div> |
|||
<div class="right"> |
|||
<table class="field-list"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Model</th> |
|||
<th>Options</th> |
|||
<th></th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div class="clear"></div> |
|||
</div> |
|||
<div class="footer"> |
|||
<div class="left"></div> |
|||
<div class="right"><button id="clear"><span class="fa fa-eraser"></span> Clear</button></div> |
|||
</div> |
|||
<ul class="context-menu"> |
|||
|
|||
<li><input type="checkbox" id="column-checkbox"/> Column</li> |
|||
<li><input type="checkbox" id="row-checkbox"/> Row</li> |
|||
<li><input type="checkbox" id="measure-checkbox"/> Measure</li> |
|||
<li><input type="checkbox" id="list-checkbox"/> List</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
</t> |
|||
</templates> |
@ -1,5 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import test_bi_view |
@ -1,5 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import wizard_ir_model_menu_create |
Write
Preview
Loading…
Cancel
Save
Reference in new issue