Enric Tobella
4 years ago
26 changed files with 955 additions and 845 deletions
-
85kpi_dashboard/demo/demo_dashboard.xml
-
87kpi_dashboard/models/kpi_dashboard.py
-
148kpi_dashboard/models/kpi_kpi.py
-
18kpi_dashboard/security/security.xml
-
115kpi_dashboard/static/src/js/dashboard_controller.js
-
18kpi_dashboard/static/src/js/dashboard_model.js
-
99kpi_dashboard/static/src/js/dashboard_renderer.js
-
36kpi_dashboard/static/src/js/dashboard_view.js
-
67kpi_dashboard/static/src/js/field_widget.js
-
88kpi_dashboard/static/src/js/widget/abstract_widget.js
-
10kpi_dashboard/static/src/js/widget/counter_widget.js
-
91kpi_dashboard/static/src/js/widget/graph_widget.js
-
57kpi_dashboard/static/src/js/widget/integer_widget.js
-
33kpi_dashboard/static/src/js/widget/meter_widget.js
-
17kpi_dashboard/static/src/js/widget/number_widget.js
-
13kpi_dashboard/static/src/js/widget/text_widget.js
-
4kpi_dashboard/static/src/js/widget_registry.js
-
40kpi_dashboard/static/src/scss/kpi_dashboard.scss
-
51kpi_dashboard/static/src/xml/dashboard.xml
-
84kpi_dashboard/templates/assets.xml
-
24kpi_dashboard/tests/test_formula.py
-
176kpi_dashboard/tests/test_kpi_dashboard.py
-
139kpi_dashboard/views/kpi_dashboard.xml
-
162kpi_dashboard/views/kpi_kpi.xml
-
9kpi_dashboard/views/kpi_menu.xml
-
21kpi_dashboard/wizards/kpi_dashboard_menu.xml
@ -1,22 +1,24 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
<odoo> |
<odoo> |
||||
<record id="group_kpi_dashboard_manager" model="res.groups"> |
<record id="group_kpi_dashboard_manager" model="res.groups"> |
||||
<field name="name">Manage KPI Dashboards</field> |
<field name="name">Manage KPI Dashboards</field> |
||||
<field name="category_id" ref="base.module_category_hidden"/> |
|
||||
<field name="users" eval="[(4, ref('base.user_admin'))]"/> |
|
||||
|
<field name="category_id" ref="base.module_category_hidden" /> |
||||
|
<field name="users" eval="[(4, ref('base.user_admin'))]" /> |
||||
</record> |
</record> |
||||
<data noupdate="1"> |
<data noupdate="1"> |
||||
<record id="rule_kpi_dashboard" model="ir.rule"> |
<record id="rule_kpi_dashboard" model="ir.rule"> |
||||
<field name="name">KPI Dashboard: User</field> |
<field name="name">KPI Dashboard: User</field> |
||||
<field name="model_id" ref="model_kpi_dashboard"/> |
|
||||
<field name="domain_force">['|', ('group_ids', '=', False), ('group_ids', 'in', user.groups_id.ids)]</field> |
|
||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/> |
|
||||
|
<field name="model_id" ref="model_kpi_dashboard" /> |
||||
|
<field |
||||
|
name="domain_force" |
||||
|
>['|', ('group_ids', '=', False), ('group_ids', 'in', user.groups_id.ids)]</field> |
||||
|
<field name="groups" eval="[(4, ref('base.group_user'))]" /> |
||||
</record> |
</record> |
||||
<record id="rule_kpi_dashboard_all" model="ir.rule"> |
<record id="rule_kpi_dashboard_all" model="ir.rule"> |
||||
<field name="name">KPI Dashboard: All</field> |
<field name="name">KPI Dashboard: All</field> |
||||
<field name="model_id" ref="model_kpi_dashboard"/> |
|
||||
|
<field name="model_id" ref="model_kpi_dashboard" /> |
||||
<field name="domain_force">[(1, '=', 1)]</field> |
<field name="domain_force">[(1, '=', 1)]</field> |
||||
<field name="groups" eval="[(4, ref('group_kpi_dashboard_manager'))]"/> |
|
||||
|
<field name="groups" eval="[(4, ref('group_kpi_dashboard_manager'))]" /> |
||||
</record> |
</record> |
||||
</data> |
</data> |
||||
</odoo> |
</odoo> |
@ -1,150 +1,159 @@ |
|||||
odoo.define('kpi_dashboard.DashboardController', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.DashboardController", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var BasicController = require('web.BasicController'); |
|
||||
var core = require('web.core'); |
|
||||
|
var BasicController = require("web.BasicController"); |
||||
|
var core = require("web.core"); |
||||
var qweb = core.qweb; |
var qweb = core.qweb; |
||||
|
|
||||
var _t = core._t; |
var _t = core._t; |
||||
|
|
||||
var DashboardController = BasicController.extend({ |
var DashboardController = BasicController.extend({ |
||||
init: function () { |
|
||||
|
init: function() { |
||||
this._super.apply(this, arguments); |
this._super.apply(this, arguments); |
||||
this.dashboard_context = {}; |
this.dashboard_context = {}; |
||||
this.dashboard_color_data = [] |
|
||||
|
this.dashboard_color_data = []; |
||||
}, |
}, |
||||
custom_events: _.extend({}, BasicController.prototype.custom_events, { |
custom_events: _.extend({}, BasicController.prototype.custom_events, { |
||||
addDashboard: '_addDashboard', |
|
||||
refresh_on_fly: '_refreshOnFly', |
|
||||
modify_context: '_modifyContext', |
|
||||
add_modify_color: '_addModifyColor', |
|
||||
refresh_colors: '_refreshColors', |
|
||||
|
addDashboard: "_addDashboard", |
||||
|
refresh_on_fly: "_refreshOnFly", |
||||
|
modify_context: "_modifyContext", |
||||
|
add_modify_color: "_addModifyColor", |
||||
|
refresh_colors: "_refreshColors", |
||||
}), |
}), |
||||
_refreshOnFly: function (event) { |
|
||||
|
_refreshOnFly: function(event) { |
||||
var self = this; |
var self = this; |
||||
this._rpc({ |
this._rpc({ |
||||
model: this.modelName, |
model: this.modelName, |
||||
method: 'read_dashboard_on_fly', |
|
||||
|
method: "read_dashboard_on_fly", |
||||
args: [[this.renderer.state.res_id]], |
args: [[this.renderer.state.res_id]], |
||||
context: this._getContext(), |
context: this._getContext(), |
||||
}).then(function (data) { |
|
||||
_.each(data, function (item) { |
|
||||
|
}).then(function(data) { |
||||
|
_.each(data, function(item) { |
||||
// We will follow the same logic used on Bus Notifications
|
// We will follow the same logic used on Bus Notifications
|
||||
self.renderer._onNotification([[ |
|
||||
"kpi_dashboard_" + self.renderer.state.res_id, |
|
||||
item |
|
||||
]]) |
|
||||
|
self.renderer._onNotification([ |
||||
|
["kpi_dashboard_" + self.renderer.state.res_id, item], |
||||
|
]); |
||||
}); |
}); |
||||
}); |
}); |
||||
}, |
}, |
||||
renderPager: function ($node, options) { |
|
||||
|
renderPager: function($node, options) { |
||||
options = _.extend({}, options, { |
options = _.extend({}, options, { |
||||
validate: this.canBeDiscarded.bind(this), |
validate: this.canBeDiscarded.bind(this), |
||||
}); |
}); |
||||
this._super($node, options); |
this._super($node, options); |
||||
}, |
}, |
||||
_pushState: function (state) { |
|
||||
|
_pushState: function(state) { |
||||
state = state || {}; |
state = state || {}; |
||||
var env = this.model.get(this.handle, {env: true}); |
var env = this.model.get(this.handle, {env: true}); |
||||
state.id = env.currentId; |
state.id = env.currentId; |
||||
this._super(state); |
this._super(state); |
||||
}, |
}, |
||||
_addDashboard: function () { |
|
||||
|
_addDashboard: function() { |
||||
var self = this; |
var self = this; |
||||
var action = self.initialState.specialData.action_id; |
var action = self.initialState.specialData.action_id; |
||||
var name = self.initialState.specialData.name; |
var name = self.initialState.specialData.name; |
||||
if (! action) { |
|
||||
|
if (!action) { |
||||
self.do_warn(_t("First you must create the Menu")); |
self.do_warn(_t("First you must create the Menu")); |
||||
} |
} |
||||
return self._rpc({ |
|
||||
route: '/board/add_to_dashboard', |
|
||||
|
return self |
||||
|
._rpc({ |
||||
|
route: "/board/add_to_dashboard", |
||||
params: { |
params: { |
||||
action_id: action, |
action_id: action, |
||||
context_to_save: {'res_id': self.initialState.res_id}, |
|
||||
domain: [('id', '=', self.initialState.res_id)], |
|
||||
view_mode: 'dashboard', |
|
||||
|
context_to_save: {res_id: self.initialState.res_id}, |
||||
|
domain: [("id", "=", self.initialState.res_id)], |
||||
|
view_mode: "dashboard", |
||||
name: name, |
name: name, |
||||
}, |
}, |
||||
}) |
}) |
||||
.then(function (r) { |
|
||||
|
.then(function(r) { |
||||
if (r) { |
if (r) { |
||||
self.do_notify( |
self.do_notify( |
||||
_.str.sprintf(_t("'%s' added to dashboard"), name), |
_.str.sprintf(_t("'%s' added to dashboard"), name), |
||||
_t('Please refresh your browser for the changes to take effect.') |
|
||||
|
_t( |
||||
|
"Please refresh your browser for the changes to take effect." |
||||
|
) |
||||
); |
); |
||||
} else { |
} else { |
||||
self.do_warn(_t("Could not add KPI dashboard to dashboard")); |
self.do_warn(_t("Could not add KPI dashboard to dashboard")); |
||||
} |
} |
||||
}); |
}); |
||||
}, |
}, |
||||
_updateButtons: function () { |
|
||||
|
_updateButtons: function() { |
||||
// HOOK Function
|
// HOOK Function
|
||||
this.$buttons.on( |
this.$buttons.on( |
||||
'click', '.o_dashboard_button_add', |
|
||||
this._addDashboard.bind(this)); |
|
||||
|
"click", |
||||
|
".o_dashboard_button_add", |
||||
|
this._addDashboard.bind(this) |
||||
|
); |
||||
}, |
}, |
||||
renderButtons: function ($node) { |
|
||||
if (! $node) { |
|
||||
|
renderButtons: function($node) { |
||||
|
if (!$node) { |
||||
return; |
return; |
||||
} |
} |
||||
|
|
||||
this.$buttons = $('<div/>'); |
|
||||
this.$buttons.append(qweb.render( |
|
||||
"kpi_dashboard.buttons", {widget: this})); |
|
||||
|
this.$buttons = $("<div/>"); |
||||
|
this.$buttons.append(qweb.render("kpi_dashboard.buttons", {widget: this})); |
||||
|
|
||||
this._updateButtons(); |
this._updateButtons(); |
||||
this.$buttons.appendTo($node); |
this.$buttons.appendTo($node); |
||||
}, |
}, |
||||
_getContext: function () { |
|
||||
|
_getContext: function() { |
||||
return _.extend( |
return _.extend( |
||||
{}, |
{}, |
||||
this.model.get(this.handle, {raw: true}).getContext(), |
this.model.get(this.handle, {raw: true}).getContext(), |
||||
{bin_size: true}, |
{bin_size: true}, |
||||
this.dashboard_context, |
|
||||
) |
|
||||
|
this.dashboard_context |
||||
|
); |
||||
}, |
}, |
||||
_modifyContext: function (event) { |
|
||||
|
_modifyContext: function(event) { |
||||
var ctx = this._getContext(); |
var ctx = this._getContext(); |
||||
this.dashboard_context = _.extend( |
this.dashboard_context = _.extend( |
||||
this.dashboard_context, |
this.dashboard_context, |
||||
py.eval(event.data.context, {context: _.extend( |
|
||||
|
py.eval(event.data.context, { |
||||
|
context: _.extend( |
||||
ctx, |
ctx, |
||||
{__getattr__: function() {return false}} |
|
||||
|
{ |
||||
|
__getattr__: function() { |
||||
|
return false; |
||||
|
}, |
||||
|
} |
||||
// We need to add this in order to allow to use undefined
|
// We need to add this in order to allow to use undefined
|
||||
// context items
|
// context items
|
||||
)}), |
|
||||
|
), |
||||
|
}) |
||||
); |
); |
||||
this._refreshOnFly(event); |
this._refreshOnFly(event); |
||||
this._refreshColors(); |
this._refreshColors(); |
||||
}, |
}, |
||||
_addModifyColor: function (event) { |
|
||||
|
_addModifyColor: function(event) { |
||||
this.dashboard_color_data.push([ |
this.dashboard_color_data.push([ |
||||
event.data.element_id, |
event.data.element_id, |
||||
event.data.expression, |
event.data.expression, |
||||
]); |
]); |
||||
}, |
}, |
||||
_refreshColors: function () { |
|
||||
|
_refreshColors: function() { |
||||
var self = this; |
var self = this; |
||||
var ctx = this._getContext(); |
var ctx = this._getContext(); |
||||
_.each(this.dashboard_color_data, function (data) { |
|
||||
|
_.each(this.dashboard_color_data, function(data) { |
||||
var color = py.eval(data[1], { |
var color = py.eval(data[1], { |
||||
context: _.extend(ctx, { |
context: _.extend(ctx, { |
||||
__getattr__: function() {return false}, |
|
||||
|
|
||||
|
__getattr__: function() { |
||||
|
return false; |
||||
|
}, |
||||
}), |
}), |
||||
check_if: function(args) { |
check_if: function(args) { |
||||
if (args[0].toJSON()) { |
if (args[0].toJSON()) { |
||||
return args[1]; |
return args[1]; |
||||
} |
} |
||||
return args[2]; |
return args[2]; |
||||
} |
|
||||
|
}, |
||||
}); |
}); |
||||
var $element = self.renderer.$el.find('#' + data[0]); |
|
||||
$element.css('background-color', color); |
|
||||
|
var $element = self.renderer.$el.find("#" + data[0]); |
||||
|
$element.css("background-color", color); |
||||
}); |
}); |
||||
}, |
}, |
||||
}); |
}); |
||||
|
|
||||
return DashboardController; |
return DashboardController; |
||||
|
|
||||
}); |
}); |
@ -1,23 +1,21 @@ |
|||||
odoo.define('kpi_dashboard.DashboardModel', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.DashboardModel", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var BasicModel = require('web.BasicModel'); |
|
||||
|
var BasicModel = require("web.BasicModel"); |
||||
|
|
||||
var DashboardModel = BasicModel.extend({ |
var DashboardModel = BasicModel.extend({ |
||||
_fetchRecord: function (record, options) { |
|
||||
|
_fetchRecord: function(record, options) { |
||||
return this._rpc({ |
return this._rpc({ |
||||
model: record.model, |
model: record.model, |
||||
method: 'read_dashboard', |
|
||||
|
method: "read_dashboard", |
||||
args: [[record.res_id]], |
args: [[record.res_id]], |
||||
context: _.extend({}, record.getContext(), {bin_size: true}), |
context: _.extend({}, record.getContext(), {bin_size: true}), |
||||
}) |
|
||||
.then(function (result) { |
|
||||
|
}).then(function(result) { |
||||
record.specialData = result; |
record.specialData = result; |
||||
return result |
|
||||
}) |
|
||||
} |
|
||||
|
return result; |
||||
|
}); |
||||
|
}, |
||||
}); |
}); |
||||
|
|
||||
return DashboardModel; |
return DashboardModel; |
||||
|
|
||||
}); |
}); |
@ -1,14 +1,14 @@ |
|||||
odoo.define('kpi_dashboard.CounterWidget', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.CounterWidget", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var IntegerWidget = require('kpi_dashboard.IntegerWidget'); |
|
||||
var registry = require('kpi_dashboard.widget_registry'); |
|
||||
var field_utils = require('web.field_utils'); |
|
||||
|
var IntegerWidget = require("kpi_dashboard.IntegerWidget"); |
||||
|
var registry = require("kpi_dashboard.widget_registry"); |
||||
|
var field_utils = require("web.field_utils"); |
||||
|
|
||||
var CounterWidget = IntegerWidget.extend({ |
var CounterWidget = IntegerWidget.extend({ |
||||
shortList: [], |
shortList: [], |
||||
}); |
}); |
||||
|
|
||||
registry.add('counter', CounterWidget); |
|
||||
|
registry.add("counter", CounterWidget); |
||||
return CounterWidget; |
return CounterWidget; |
||||
}); |
}); |
@ -1,39 +1,34 @@ |
|||||
odoo.define('kpi_dashboard.MeterWidget', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.MeterWidget", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var AbstractWidget = require('kpi_dashboard.AbstractWidget'); |
|
||||
var registry = require('kpi_dashboard.widget_registry'); |
|
||||
|
|
||||
|
var AbstractWidget = require("kpi_dashboard.AbstractWidget"); |
||||
|
var registry = require("kpi_dashboard.widget_registry"); |
||||
|
|
||||
var MeterWidget = AbstractWidget.extend({ |
var MeterWidget = AbstractWidget.extend({ |
||||
template: 'kpi_dashboard.meter', |
|
||||
jsLibs: [ |
|
||||
'/kpi_dashboard/static/lib/gauge/GaugeMeter.js', |
|
||||
], |
|
||||
fillWidget: function (values) { |
|
||||
|
template: "kpi_dashboard.meter", |
||||
|
jsLibs: ["/kpi_dashboard/static/lib/gauge/GaugeMeter.js"], |
||||
|
fillWidget: function(values) { |
||||
var input = this.$el.find('[data-bind="value"]'); |
var input = this.$el.find('[data-bind="value"]'); |
||||
var options = this._getMeterOptions(values); |
var options = this._getMeterOptions(values); |
||||
var margin = (this.widget_dimension_x - options.size)/2; |
|
||||
|
var margin = (this.widget_dimension_x - options.size) / 2; |
||||
input.gaugeMeter(options); |
input.gaugeMeter(options); |
||||
input.parent().css('padding-left', margin); |
|
||||
|
input.parent().css("padding-left", margin); |
||||
}, |
}, |
||||
_getMeterOptions: function (values) { |
|
||||
var size = Math.min( |
|
||||
this.widget_size_x, |
|
||||
this.widget_size_y - 40) - 10; |
|
||||
|
_getMeterOptions: function(values) { |
||||
|
var size = Math.min(this.widget_size_x, this.widget_size_y - 40) - 10; |
||||
return { |
return { |
||||
percent: values.value.value, |
percent: values.value.value, |
||||
style: 'Arch', |
|
||||
|
style: "Arch", |
||||
width: 10, |
width: 10, |
||||
size: size, |
size: size, |
||||
prepend: values.prefix !== undefined ? values.prefix : '', |
|
||||
append: values.suffix !== undefined ? values.suffix : '', |
|
||||
|
prepend: values.prefix !== undefined ? values.prefix : "", |
||||
|
append: values.suffix !== undefined ? values.suffix : "", |
||||
color: values.font_color, |
color: values.font_color, |
||||
animate_text_colors: true, |
animate_text_colors: true, |
||||
}; |
}; |
||||
}, |
}, |
||||
}); |
}); |
||||
|
|
||||
registry.add('meter', MeterWidget); |
|
||||
|
registry.add("meter", MeterWidget); |
||||
return MeterWidget; |
return MeterWidget; |
||||
}); |
}); |
@ -1,21 +1,22 @@ |
|||||
odoo.define('kpi_dashboard.NumberWidget', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.NumberWidget", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var IntegerWidget = require('kpi_dashboard.IntegerWidget'); |
|
||||
var registry = require('kpi_dashboard.widget_registry'); |
|
||||
var field_utils = require('web.field_utils'); |
|
||||
|
var IntegerWidget = require("kpi_dashboard.IntegerWidget"); |
||||
|
var registry = require("kpi_dashboard.widget_registry"); |
||||
|
var field_utils = require("web.field_utils"); |
||||
|
|
||||
var NumberWidget = IntegerWidget.extend({ |
var NumberWidget = IntegerWidget.extend({ |
||||
digits: [3, 1], |
digits: [3, 1], |
||||
shortNumber: function (num) { |
|
||||
|
shortNumber: function(num) { |
||||
if (Math.abs(num) < 10) { |
if (Math.abs(num) < 10) { |
||||
return field_utils.format.float(num, false, { |
return field_utils.format.float(num, false, { |
||||
digits: [3, 2]}); |
|
||||
|
digits: [3, 2], |
||||
|
}); |
||||
} |
} |
||||
return this._super.apply(this, arguments) |
|
||||
|
return this._super.apply(this, arguments); |
||||
}, |
}, |
||||
}); |
}); |
||||
|
|
||||
registry.add('number', NumberWidget); |
|
||||
|
registry.add("number", NumberWidget); |
||||
return NumberWidget; |
return NumberWidget; |
||||
}); |
}); |
@ -1,17 +1,16 @@ |
|||||
odoo.define('kpi_dashboard.TextWidget', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.TextWidget", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var AbstractWidget = require('kpi_dashboard.AbstractWidget'); |
|
||||
var registry = require('kpi_dashboard.widget_registry'); |
|
||||
|
|
||||
|
var AbstractWidget = require("kpi_dashboard.AbstractWidget"); |
||||
|
var registry = require("kpi_dashboard.widget_registry"); |
||||
|
|
||||
var TextWidget = AbstractWidget.extend({ |
var TextWidget = AbstractWidget.extend({ |
||||
template: 'kpi_dashboard.base_text', |
|
||||
fillWidget: function () { |
|
||||
|
template: "kpi_dashboard.base_text", |
||||
|
fillWidget: function() { |
||||
return; |
return; |
||||
}, |
}, |
||||
}); |
}); |
||||
|
|
||||
registry.add('base_text', TextWidget); |
|
||||
|
registry.add("base_text", TextWidget); |
||||
return TextWidget; |
return TextWidget; |
||||
}); |
}); |
@ -1,7 +1,7 @@ |
|||||
odoo.define('kpi_dashboard.widget_registry', function (require) { |
|
||||
|
odoo.define("kpi_dashboard.widget_registry", function(require) { |
||||
"use strict"; |
"use strict"; |
||||
|
|
||||
var Registry = require('web.Registry'); |
|
||||
|
var Registry = require("web.Registry"); |
||||
|
|
||||
return new Registry(); |
return new Registry(); |
||||
}); |
}); |
@ -1,28 +1,68 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
<odoo> |
<odoo> |
||||
|
|
||||
<template id="assets_backend" |
|
||||
|
<template |
||||
|
id="assets_backend" |
||||
name="Backend Assets (used in backend interface)" |
name="Backend Assets (used in backend interface)" |
||||
inherit_id="web.assets_backend"> |
|
||||
|
inherit_id="web.assets_backend" |
||||
|
> |
||||
<xpath expr="." position="inside"> |
<xpath expr="." position="inside"> |
||||
|
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget_registry.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/abstract_widget.js"/> |
|
||||
|
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/dashboard_renderer.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/dashboard_model.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/dashboard_controller.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/dashboard_view.js"/> |
|
||||
|
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/integer_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/number_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/counter_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/meter_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/graph_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/widget/text_widget.js"/> |
|
||||
<script type="text/javascript" src="/kpi_dashboard/static/src/js/field_widget.js"/> |
|
||||
|
|
||||
<link rel="stylesheet" type="text/scss" href="/kpi_dashboard/static/src/scss/kpi_dashboard.scss"/> |
|
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget_registry.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/abstract_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/dashboard_renderer.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/dashboard_model.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/dashboard_controller.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/dashboard_view.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/integer_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/number_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/counter_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/meter_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/graph_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/widget/text_widget.js" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="/kpi_dashboard/static/src/js/field_widget.js" |
||||
|
/> |
||||
|
<link |
||||
|
rel="stylesheet" |
||||
|
type="text/scss" |
||||
|
href="/kpi_dashboard/static/src/scss/kpi_dashboard.scss" |
||||
|
/> |
||||
</xpath> |
</xpath> |
||||
</template> |
</template> |
||||
</odoo> |
</odoo> |
@ -1,12 +1,13 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
<!-- Copyright 2020 Creu Blanca |
<!-- Copyright 2020 Creu Blanca |
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
||||
|
|
||||
<odoo> |
<odoo> |
||||
<!-- CONFIGURATION --> |
<!-- CONFIGURATION --> |
||||
<menuitem id="menu_configuration_kpi_dashboards" |
|
||||
|
<menuitem |
||||
|
id="menu_configuration_kpi_dashboards" |
||||
name="KPI Dashboards" |
name="KPI Dashboards" |
||||
parent="base.menu_reporting_config" |
parent="base.menu_reporting_config" |
||||
groups="kpi_dashboard.group_kpi_dashboard_manager" |
groups="kpi_dashboard.group_kpi_dashboard_manager" |
||||
sequence="10"/> |
|
||||
|
sequence="10" |
||||
|
/> |
||||
</odoo> |
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue