diff --git a/kpi_dashboard/__manifest__.py b/kpi_dashboard/__manifest__.py
index 2046ee367..7ad4e1564 100644
--- a/kpi_dashboard/__manifest__.py
+++ b/kpi_dashboard/__manifest__.py
@@ -15,8 +15,8 @@
"wizards/kpi_dashboard_menu.xml",
"security/security.xml",
"security/ir.model.access.csv",
+ "templates/assets.xml",
"views/kpi_menu.xml",
- "views/webclient_templates.xml",
"views/kpi_kpi.xml",
"views/kpi_dashboard.xml",
],
diff --git a/kpi_dashboard/demo/demo_dashboard.xml b/kpi_dashboard/demo/demo_dashboard.xml
index 958e15806..3a31344cc 100644
--- a/kpi_dashboard/demo/demo_dashboard.xml
+++ b/kpi_dashboard/demo/demo_dashboard.xml
@@ -6,6 +6,7 @@
50
250
#020202
+ 30
@@ -87,6 +88,26 @@ result = {"graphs": [
+
+ Integer counter
+ code
+ integer
+
+
+result = {"value": self.env.context.get('counter', 990)}
+
+
+
+
+ Counter
+ code
+ counter
+
+
+result = {"value": self.env.context.get('counter', 990)}
+
+
+
Dashboard title
@@ -141,6 +162,43 @@ result = {"graphs": [
#ffffff
+
+ +1 to Counter
+
+ 3
+ 10
+ 1
+ 2
+ #B41F1F
+ #EEBF77
+
+ {'counter': (context.counter or 990) + 1}
+
+ check_if(((context.counter or 990) + 1) % 2, '#ff0000', '#00ff00')
+
+
+
+ Counter
+
+
+ 3
+ 11
+ 3
+ #4B0082
+ #ffffff
+
+
+
+ Integer
+
+
+ 4
+ 11
+ 3
+ #ffffff
+ #4B0082
+
+
Graph
diff --git a/kpi_dashboard/models/kpi_dashboard.py b/kpi_dashboard/models/kpi_dashboard.py
index 770a25464..24d423017 100644
--- a/kpi_dashboard/models/kpi_dashboard.py
+++ b/kpi_dashboard/models/kpi_dashboard.py
@@ -117,6 +117,10 @@ class KpiDashboardItem(models.Model):
size_y = fields.Integer(required=True, default=1)
color = fields.Char()
font_color = fields.Char()
+ modify_context = fields.Boolean()
+ modify_context_expression = fields.Char()
+ modify_color = fields.Boolean()
+ modify_color_expression = fields.Char()
@api.depends('row', 'size_y')
def _compute_end_row(self):
@@ -173,7 +177,13 @@ class KpiDashboardItem(models.Model):
"sizey": self.size_y,
"color": self.color,
"font_color": self.font_color or "000000",
+ "modify_context": self.modify_context,
+ "modify_color": self.modify_color,
}
+ if self.modify_context:
+ vals['modify_context_expression'] = self.modify_context_expression
+ if self.modify_color:
+ vals['modify_color_expression'] = self.modify_color_expression
if self.kpi_id:
vals.update(
{
@@ -205,3 +215,16 @@ class KpiDashboardItem(models.Model):
for kpi in self:
result.append(kpi._read_dashboard())
return result
+
+ def technical_config(self):
+ self.ensure_one()
+ return {
+ 'name': self.display_name,
+ 'res_model': self._name,
+ 'res_id': self.id,
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'view_id': self.env.ref(
+ 'kpi_dashboard.kpi_dashboard_item_config_form_view').id,
+ }
diff --git a/kpi_dashboard/models/kpi_kpi.py b/kpi_dashboard/models/kpi_kpi.py
index b67e506a2..a16d613d0 100644
--- a/kpi_dashboard/models/kpi_kpi.py
+++ b/kpi_dashboard/models/kpi_kpi.py
@@ -10,6 +10,7 @@ from odoo.tools.float_utils import float_compare
import re
import json
import datetime
+from dateutil import relativedelta
class KpiKpi(models.Model):
@@ -29,7 +30,8 @@ class KpiKpi(models.Model):
args = fields.Char()
kwargs = fields.Char()
widget = fields.Selection(
- [("number", "Number"), ("meter", "Meter"), ("graph", "Graph")],
+ [('integer', 'Integer'), ("number", "Number"), ("meter", "Meter"),
+ ('counter', 'Counter'), ("graph", "Graph")],
required=True,
default="number",
)
@@ -136,6 +138,7 @@ class KpiKpi(models.Model):
"model": self.browse(),
"datetime": datetime,
"float_compare": float_compare,
+ "relativedelta": relativedelta.relativedelta,
}
def _forbidden_code(self):
diff --git a/kpi_dashboard/static/src/js/dashboard_controller.js b/kpi_dashboard/static/src/js/dashboard_controller.js
index 952077bda..97db1765d 100644
--- a/kpi_dashboard/static/src/js/dashboard_controller.js
+++ b/kpi_dashboard/static/src/js/dashboard_controller.js
@@ -8,9 +8,17 @@ odoo.define('kpi_dashboard.DashboardController', function (require) {
var _t = core._t;
var DashboardController = BasicController.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+ this.dashboard_context = {};
+ this.dashboard_color_data = []
+ },
custom_events: _.extend({}, BasicController.prototype.custom_events, {
addDashboard: '_addDashboard',
refresh_on_fly: '_refreshOnFly',
+ modify_context: '_modifyContext',
+ add_modify_color: '_addModifyColor',
+ refresh_colors: '_refreshColors',
}),
_refreshOnFly: function (event) {
var self = this;
@@ -18,11 +26,7 @@ odoo.define('kpi_dashboard.DashboardController', function (require) {
model: this.modelName,
method: 'read_dashboard_on_fly',
args: [[this.renderer.state.res_id]],
- context: _.extend(
- {},
- this.model.get(this.handle, {raw: true}).getContext(),
- {bin_size: true}
- ),
+ context: this._getContext(),
}).then(function (data) {
_.each(data, function (item) {
// We will follow the same logic used on Bus Notifications
@@ -91,6 +95,54 @@ odoo.define('kpi_dashboard.DashboardController', function (require) {
this._updateButtons();
this.$buttons.appendTo($node);
},
+ _getContext: function () {
+ return _.extend(
+ {},
+ this.model.get(this.handle, {raw: true}).getContext(),
+ {bin_size: true},
+ this.dashboard_context,
+ )
+ },
+ _modifyContext: function (event) {
+ var ctx = this._getContext();
+ this.dashboard_context = _.extend(
+ this.dashboard_context,
+ py.eval(event.data.context, {context: _.extend(
+ ctx,
+ {__getattr__: function() {return false}}
+ // We need to add this in order to allow to use undefined
+ // context items
+ )}),
+ );
+ this._refreshOnFly(event);
+ this._refreshColors();
+ },
+ _addModifyColor: function (event) {
+ this.dashboard_color_data.push([
+ event.data.element_id,
+ event.data.expression,
+ ]);
+ },
+ _refreshColors: function () {
+ var self = this;
+ var ctx = this._getContext();
+ _.each(this.dashboard_color_data, function (data) {
+ var color = py.eval(data[1], {
+ context: _.extend(ctx, {
+ __getattr__: function() {return false},
+
+ }),
+ check_if: function(args) {
+ if (args[0].toJSON()) {
+ return args[1];
+ }
+ return args[2];
+ }
+ });
+ var $element = self.renderer.$el.find('#' + data[0]);
+ $element.css('background-color', color);
+ });
+ },
});
return DashboardController;
diff --git a/kpi_dashboard/static/src/js/dashboard_renderer.js b/kpi_dashboard/static/src/js/dashboard_renderer.js
index b26037716..6bc38c492 100644
--- a/kpi_dashboard/static/src/js/dashboard_renderer.js
+++ b/kpi_dashboard/static/src/js/dashboard_renderer.js
@@ -16,6 +16,12 @@ odoo.define('kpi_dashboard.DashboardRenderer', function (require) {
var widget = new Widget(this, kpi);
return widget;
},
+ _onClickModifyContext: function (modify_context_expression, event) {
+ this.trigger_up('modify_context', {
+ context: modify_context_expression,
+ event: event,
+ })
+ },
_renderView: function () {
this.$el.html($(qweb.render('dashboard_kpi.dashboard')));
this.$el.css(
@@ -30,7 +36,20 @@ odoo.define('kpi_dashboard.DashboardRenderer', function (require) {
'kpi_dashboard.kpi', {widget: kpi}));
element.css('background-color', kpi.color);
element.css('color', kpi.font_color);
+ element.attr('id', _.uniqueId('kpi_'));
self.$grid.append(element);
+ if (kpi.modify_color) {
+ self.trigger_up("add_modify_color", {
+ element_id: element.attr("id"),
+ expression: kpi.modify_color_expression,
+ })
+ }
+ if (kpi.modify_context) {
+ element.on("click", self._onClickModifyContext.bind(
+ self, kpi.modify_context_expression));
+ element.css('cursor', 'pointer');
+ // We want to set it show as clickable
+ }
self.kpi_widget[kpi.id] = self._getDashboardWidget(kpi);
self.kpi_widget[kpi.id].appendTo(element);
});
@@ -59,6 +78,10 @@ odoo.define('kpi_dashboard.DashboardRenderer', function (require) {
self.trigger_up('refresh_on_fly');
}, this.state.specialData.compute_on_fly_refresh *1000);
};
+ this.trigger_up('refresh_colors');
+ this.trigger_up('refresh_on_fly');
+ // We need to refreshs data in order compute with the current
+ // context
return $.when();
},
on_detach_callback: function () {
diff --git a/kpi_dashboard/static/src/js/widget/counter_widget.js b/kpi_dashboard/static/src/js/widget/counter_widget.js
new file mode 100644
index 000000000..6005298f2
--- /dev/null
+++ b/kpi_dashboard/static/src/js/widget/counter_widget.js
@@ -0,0 +1,14 @@
+odoo.define('kpi_dashboard.CounterWidget', function (require) {
+ "use strict";
+
+ var IntegerWidget = require('kpi_dashboard.IntegerWidget');
+ var registry = require('kpi_dashboard.widget_registry');
+ var field_utils = require('web.field_utils');
+
+ var CounterWidget = IntegerWidget.extend({
+ shortList: [],
+ });
+
+ registry.add('counter', CounterWidget);
+ return CounterWidget;
+});
diff --git a/kpi_dashboard/static/src/js/widget/integer_widget.js b/kpi_dashboard/static/src/js/widget/integer_widget.js
new file mode 100644
index 000000000..7b9354d61
--- /dev/null
+++ b/kpi_dashboard/static/src/js/widget/integer_widget.js
@@ -0,0 +1,71 @@
+odoo.define('kpi_dashboard.IntegerWidget', function (require) {
+ "use strict";
+
+ var AbstractWidget = require('kpi_dashboard.AbstractWidget');
+ var registry = require('kpi_dashboard.widget_registry');
+ var field_utils = require('web.field_utils');
+
+
+ var IntegerWidget = AbstractWidget.extend({
+ template: 'kpi_dashboard.number',
+ digits: [3, 0],
+ shortList: [
+ [1000000000000, 'T', [3, 1]],
+ [1000000000, 'G', [3, 1]],
+ [1000000, 'M', [3, 1]],
+ [1000, 'K', [3, 1]]
+ ],
+ shortNumber: function (num) {
+ var suffix = '';
+ var shortened = false;
+ var digits = this.digits;
+ _.each(this.shortList, function (shortItem) {
+ if (!shortened && Math.abs(num) >= shortItem[0]) {
+ shortened = true;
+ suffix = shortItem[1];
+ num = num / shortItem[0];
+ digits = shortItem[2];
+ }
+ });
+ return field_utils.format.float(num, false, {
+ digits: digits}) + suffix;
+ },
+ fillWidget: function (values) {
+ var widget = this.$el;
+ var value = values.value.value;
+ if (value === undefined) {
+ value = 0;
+ }
+ var item = widget.find('[data-bind="value"]');
+ if (item) {
+ item.text(this.shortNumber(value));
+ }
+ var previous = values.value.previous;
+
+ var $change_rate = widget.find('.change-rate');
+ if (previous === undefined) {
+ $change_rate.toggleClass('active', false);
+ } else {
+ var difference = 0;
+ if (previous !== 0) {
+ difference = field_utils.format.integer(
+ (100 * value / previous) - 100) + '%';
+ }
+ $change_rate.toggleClass('active', true);
+ var $difference = widget.find('[data-bind="difference"]');
+ $difference.text(difference);
+ var $arrow = widget.find('[data-bind="arrow"]');
+ if (value < previous) {
+ $arrow.toggleClass('fa-arrow-up', false);
+ $arrow.toggleClass('fa-arrow-down', true);
+ } else {
+ $arrow.toggleClass('fa-arrow-up', true);
+ $arrow.toggleClass('fa-arrow-down', false);
+ }
+ }
+ },
+ });
+
+ registry.add('integer', IntegerWidget);
+ return IntegerWidget;
+});
diff --git a/kpi_dashboard/static/src/js/widget/number_widget.js b/kpi_dashboard/static/src/js/widget/number_widget.js
index 987c612e5..569f8cf2d 100644
--- a/kpi_dashboard/static/src/js/widget/number_widget.js
+++ b/kpi_dashboard/static/src/js/widget/number_widget.js
@@ -1,72 +1,21 @@
odoo.define('kpi_dashboard.NumberWidget', function (require) {
"use strict";
- var AbstractWidget = require('kpi_dashboard.AbstractWidget');
+ var IntegerWidget = require('kpi_dashboard.IntegerWidget');
var registry = require('kpi_dashboard.widget_registry');
var field_utils = require('web.field_utils');
-
- var NumberWidget = AbstractWidget.extend({
- template: 'kpi_dashboard.number',
+ var NumberWidget = IntegerWidget.extend({
+ digits: [3, 1],
shortNumber: function (num) {
- if (Math.abs(num) >= 1000000000000) {
- return field_utils.format.integer(num / 1000000000000, false, {
- digits: [3, 1]}) + 'T';
- }
- if (Math.abs(num) >= 1000000000) {
- return field_utils.format.integer(num / 1000000000, false, {
- digits: [3,1]}) + 'G';
- }
- if (Math.abs(num) >= 1000000) {
- return field_utils.format.integer(num / 1000000, false, {
- digits: [3, 1]}) + 'M';
- }
- if (Math.abs(num) >= 1000) {
- return field_utils.format.float(num / 1000, false, {
- digits: [3, 1]}) + 'K';
- }
- if (Math.abs(num) >= 10) {
+ if (Math.abs(num) < 10) {
return field_utils.format.float(num, false, {
- digits: [3, 1]});
- }
- return field_utils.format.float(num, false, {
- digits: [3, 2]});
- },
- fillWidget: function (values) {
- var widget = this.$el;
- var value = values.value.value;
- if (value === undefined) {
- value = 0;
- }
- var item = widget.find('[data-bind="value"]');
- if (item) {
- item.text(this.shortNumber(value));
- }
- var previous = values.value.previous;
-
- var $change_rate = widget.find('.change-rate');
- if (previous === undefined) {
- $change_rate.toggleClass('active', false);
- } else {
- var difference = 0;
- if (previous !== 0) {
- difference = field_utils.format.integer(
- (100 * value / previous) - 100) + '%';
- }
- $change_rate.toggleClass('active', true);
- var $difference = widget.find('[data-bind="difference"]');
- $difference.text(difference);
- var $arrow = widget.find('[data-bind="arrow"]');
- if (value < previous) {
- $arrow.toggleClass('fa-arrow-up', false);
- $arrow.toggleClass('fa-arrow-down', true);
- } else {
- $arrow.toggleClass('fa-arrow-up', true);
- $arrow.toggleClass('fa-arrow-down', false);
- }
+ digits: [3, 2]});
}
+ return this._super.apply(this, arguments)
},
});
+
registry.add('number', NumberWidget);
return NumberWidget;
});
diff --git a/kpi_dashboard/views/webclient_templates.xml b/kpi_dashboard/templates/assets.xml
similarity index 87%
rename from kpi_dashboard/views/webclient_templates.xml
rename to kpi_dashboard/templates/assets.xml
index e4263077d..87b5e141f 100644
--- a/kpi_dashboard/views/webclient_templates.xml
+++ b/kpi_dashboard/templates/assets.xml
@@ -14,7 +14,9 @@
+
+
diff --git a/kpi_dashboard/views/kpi_dashboard.xml b/kpi_dashboard/views/kpi_dashboard.xml
index e2f841add..905431250 100644
--- a/kpi_dashboard/views/kpi_dashboard.xml
+++ b/kpi_dashboard/views/kpi_dashboard.xml
@@ -36,6 +36,9 @@
+
@@ -111,4 +114,57 @@
+
+ kpi.dashboard.item.form (in kpi_dashboard)
+ kpi.dashboard.item
+
+
+
+
+
+
+ kpi.dashboard.item.form (in kpi_dashboard)
+ kpi.dashboard.item
+
+
+
+
+