[IMP] kpi_dashboard: black, isort, prettier
parent
1d165764c5
commit
de5a435343
|
@ -8,7 +8,6 @@
|
|||
<field name="background_color">#020202</field>
|
||||
<field name="compute_on_fly_refresh">30</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_number_01" model="kpi.kpi">
|
||||
<field name="name">Number 01</field>
|
||||
<field name="prefix">$</field>
|
||||
|
@ -18,7 +17,6 @@
|
|||
result = {"value": 10000,"previous": 12000}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_number_02" model="kpi.kpi">
|
||||
<field name="name">Number 02</field>
|
||||
<field name="suffix">€</field>
|
||||
|
@ -28,10 +26,11 @@ result = {"value": 10000,"previous": 12000}
|
|||
result = {"value": 12000,"previous": 10000}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_number_01'), ref('widget_number_02')]]"/>
|
||||
|
||||
<function
|
||||
model="kpi.kpi"
|
||||
name="compute"
|
||||
eval="[[ref('widget_number_01'), ref('widget_number_02')]]"
|
||||
/>
|
||||
<record id="widget_meter_01" model="kpi.kpi">
|
||||
<field name="name">Meter 01</field>
|
||||
<field name="suffix">€</field>
|
||||
|
@ -41,7 +40,6 @@ result = {"value": 12000,"previous": 10000}
|
|||
result = {"min": 0, "max": 100, "value": 90}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_meter_02" model="kpi.kpi">
|
||||
<field name="name">Meter 02</field>
|
||||
<field name="prefix">$</field>
|
||||
|
@ -51,10 +49,11 @@ result = {"min": 0, "max": 100, "value": 90}
|
|||
result = {"min": 0, "max": 100, "value": 40}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_meter_01'), ref('widget_meter_02')]]"/>
|
||||
|
||||
<function
|
||||
model="kpi.kpi"
|
||||
name="compute"
|
||||
eval="[[ref('widget_meter_01'), ref('widget_meter_02')]]"
|
||||
/>
|
||||
<record id="widget_graph" model="kpi.kpi">
|
||||
<field name="name">Graph</field>
|
||||
<field name="computation_method">code</field>
|
||||
|
@ -84,125 +83,116 @@ result = {"graphs": [
|
|||
]}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<function model="kpi.kpi" name="compute"
|
||||
eval="[[ref('widget_graph')]]"/>
|
||||
|
||||
<function model="kpi.kpi" name="compute" eval="[[ref('widget_graph')]]" />
|
||||
<record id="widget_integer" model="kpi.kpi">
|
||||
<field name="name">Integer counter</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">integer</field>
|
||||
<field name="compute_on_fly" eval="True"/>
|
||||
<field name="compute_on_fly" eval="True" />
|
||||
<field name="code">
|
||||
result = {"value": self.env.context.get('counter', 990)}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="widget_counter" model="kpi.kpi">
|
||||
<field name="name">Counter</field>
|
||||
<field name="computation_method">code</field>
|
||||
<field name="widget">counter</field>
|
||||
<field name="compute_on_fly" eval="True"/>
|
||||
<field name="compute_on_fly" eval="True" />
|
||||
<field name="code">
|
||||
result = {"value": self.env.context.get('counter', 990)}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_text" model="kpi.dashboard.item">
|
||||
<field name="name">Dashboard title</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="column">1</field>
|
||||
<field name="row">1</field>
|
||||
<field name="size_x">4</field>
|
||||
<field name="color">#707070</field>
|
||||
<field name="font_color">#000000</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_number_01" model="kpi.dashboard.item">
|
||||
<field name="name">Number 01</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_number_01"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_number_01" />
|
||||
<field name="column">1</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#47bbb3</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_number_02" model="kpi.dashboard.item">
|
||||
<field name="name">Number 02</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_number_02"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_number_02" />
|
||||
<field name="column">1</field>
|
||||
<field name="row">6</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#ec663c</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_meter_01" model="kpi.dashboard.item">
|
||||
<field name="name">Meter 01</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_meter_01"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_meter_01" />
|
||||
<field name="column">2</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#9c4274</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_meter_02" model="kpi.dashboard.item">
|
||||
<field name="name">Meter 02</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_meter_02"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_meter_02" />
|
||||
<field name="column">2</field>
|
||||
<field name="row">6</field>
|
||||
<field name="size_y">4</field>
|
||||
<field name="color">#12b0c5</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_add_counter" model="kpi.dashboard.item">
|
||||
<field name="name">+1 to Counter</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="column">3</field>
|
||||
<field name="row">10</field>
|
||||
<field name="size_y">1</field>
|
||||
<field name="size_x">2</field>
|
||||
<field name="color">#B41F1F</field>
|
||||
<field name="font_color">#EEBF77</field>
|
||||
<field name="modify_context" eval="True"/>
|
||||
<field name="modify_context_expression">{'counter': (context.counter or 990) + 1}</field>
|
||||
<field name="modify_color" eval="True"/>
|
||||
<field name="modify_color_expression">check_if(((context.counter or 990) + 1) % 2, '#ff0000', '#00ff00')</field>
|
||||
<field name="modify_context" eval="True" />
|
||||
<field
|
||||
name="modify_context_expression"
|
||||
>{'counter': (context.counter or 990) + 1}</field>
|
||||
<field name="modify_color" eval="True" />
|
||||
<field
|
||||
name="modify_color_expression"
|
||||
>check_if(((context.counter or 990) + 1) % 2, '#ff0000', '#00ff00')</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_counter" model="kpi.dashboard.item">
|
||||
<field name="name">Counter</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_counter"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_counter" />
|
||||
<field name="column">3</field>
|
||||
<field name="row">11</field>
|
||||
<field name="size_y">3</field>
|
||||
<field name="color">#4B0082</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_integer" model="kpi.dashboard.item">
|
||||
<field name="name">Integer</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_integer"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_integer" />
|
||||
<field name="column">4</field>
|
||||
<field name="row">11</field>
|
||||
<field name="size_y">3</field>
|
||||
<field name="color">#ffffff</field>
|
||||
<field name="font_color">#4B0082</field>
|
||||
</record>
|
||||
|
||||
<record id="dashboard_widget_graph" model="kpi.dashboard.item">
|
||||
<field name="name">Graph</field>
|
||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||
<field name="kpi_id" ref="widget_graph"/>
|
||||
<field name="dashboard_id" ref="demo_dashboard" />
|
||||
<field name="kpi_id" ref="widget_graph" />
|
||||
<field name="column">3</field>
|
||||
<field name="row">2</field>
|
||||
<field name="size_x">2</field>
|
||||
|
@ -210,5 +200,4 @@ result = {"value": self.env.context.get('counter', 990)}
|
|||
<field name="color">#ff9618</field>
|
||||
<field name="font_color">#ffffff</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
|
@ -17,8 +17,7 @@ class KpiDashboard(models.Model):
|
|||
)
|
||||
number_of_columns = fields.Integer(default=5, required=True)
|
||||
compute_on_fly_refresh = fields.Integer(
|
||||
default=0,
|
||||
help="Seconds to refresh on fly elements"
|
||||
default=0, help="Seconds to refresh on fly elements"
|
||||
)
|
||||
width = fields.Integer(compute="_compute_width")
|
||||
margin_y = fields.Integer(default=10, required=True)
|
||||
|
@ -34,9 +33,7 @@ class KpiDashboard(models.Model):
|
|||
if "group_ids" in vals:
|
||||
for rec in self:
|
||||
if rec.menu_id:
|
||||
rec.menu_id.write(
|
||||
{"groups_id": [(6, 0, rec.group_ids.ids)]}
|
||||
)
|
||||
rec.menu_id.write({"groups_id": [(6, 0, rec.group_ids.ids)]})
|
||||
return res
|
||||
|
||||
@api.depends("widget_dimension_x", "margin_x", "number_of_columns")
|
||||
|
@ -78,7 +75,7 @@ class KpiDashboard(models.Model):
|
|||
return {
|
||||
"parent_id": menu.id or False,
|
||||
"name": self.name,
|
||||
"action": "%s,%s" % (action._name, action.id),
|
||||
"action": "{},{}".format(action._name, action.id),
|
||||
"groups_id": [(6, 0, self.group_ids.ids)],
|
||||
}
|
||||
|
||||
|
@ -106,13 +103,11 @@ class KpiDashboardItem(models.Model):
|
|||
|
||||
name = fields.Char(required=True)
|
||||
kpi_id = fields.Many2one("kpi.kpi")
|
||||
dashboard_id = fields.Many2one(
|
||||
"kpi.dashboard", required=True, ondelete="cascade"
|
||||
)
|
||||
dashboard_id = fields.Many2one("kpi.dashboard", required=True, ondelete="cascade")
|
||||
column = fields.Integer(required=True, default=1)
|
||||
row = fields.Integer(required=True, default=1)
|
||||
end_row = fields.Integer(store=True, compute='_compute_end_row')
|
||||
end_column = fields.Integer(store=True, compute='_compute_end_column')
|
||||
end_row = fields.Integer(store=True, compute="_compute_end_row")
|
||||
end_column = fields.Integer(store=True, compute="_compute_end_column")
|
||||
size_x = fields.Integer(required=True, default=1)
|
||||
size_y = fields.Integer(required=True, default=1)
|
||||
color = fields.Char()
|
||||
|
@ -122,44 +117,43 @@ class KpiDashboardItem(models.Model):
|
|||
modify_color = fields.Boolean()
|
||||
modify_color_expression = fields.Char()
|
||||
|
||||
@api.depends('row', 'size_y')
|
||||
@api.depends("row", "size_y")
|
||||
def _compute_end_row(self):
|
||||
for r in self:
|
||||
r.end_row = r.row + r.size_y - 1
|
||||
|
||||
@api.depends('column', 'size_x')
|
||||
@api.depends("column", "size_x")
|
||||
def _compute_end_column(self):
|
||||
for r in self:
|
||||
r.end_column = r.column + r.size_x - 1
|
||||
|
||||
@api.constrains('size_y')
|
||||
@api.constrains("size_y")
|
||||
def _check_size_y(self):
|
||||
for rec in self:
|
||||
if rec.size_y > 10:
|
||||
raise ValidationError(_(
|
||||
'Size Y of the widget cannot be bigger than 10'))
|
||||
raise ValidationError(
|
||||
_("Size Y of the widget cannot be bigger than 10")
|
||||
)
|
||||
|
||||
def _check_size_domain(self):
|
||||
return [
|
||||
('dashboard_id', '=', self.dashboard_id.id),
|
||||
('id', '!=', self.id),
|
||||
('row', '<=', self.end_row),
|
||||
('end_row', '>=', self.row),
|
||||
('column', '<=', self.end_column),
|
||||
('end_column', '>=', self.column),
|
||||
("dashboard_id", "=", self.dashboard_id.id),
|
||||
("id", "!=", self.id),
|
||||
("row", "<=", self.end_row),
|
||||
("end_row", ">=", self.row),
|
||||
("column", "<=", self.end_column),
|
||||
("end_column", ">=", self.column),
|
||||
]
|
||||
|
||||
@api.constrains('end_row', 'end_column', 'row', 'column')
|
||||
@api.constrains("end_row", "end_column", "row", "column")
|
||||
def _check_size(self):
|
||||
for r in self:
|
||||
if self.search(r._check_size_domain(), limit=1):
|
||||
raise ValidationError(_(
|
||||
'Widgets cannot be crossed by other widgets'
|
||||
))
|
||||
raise ValidationError(_("Widgets cannot be crossed by other widgets"))
|
||||
if r.end_column > r.dashboard_id.number_of_columns:
|
||||
raise ValidationError(_(
|
||||
'Widget %s is bigger than expected'
|
||||
) % r.display_name)
|
||||
raise ValidationError(
|
||||
_("Widget %s is bigger than expected") % r.display_name
|
||||
)
|
||||
|
||||
@api.onchange("kpi_id")
|
||||
def _onchange_kpi(self):
|
||||
|
@ -181,9 +175,9 @@ class KpiDashboardItem(models.Model):
|
|||
"modify_color": self.modify_color,
|
||||
}
|
||||
if self.modify_context:
|
||||
vals['modify_context_expression'] = self.modify_context_expression
|
||||
vals["modify_context_expression"] = self.modify_context_expression
|
||||
if self.modify_color:
|
||||
vals['modify_color_expression'] = self.modify_color_expression
|
||||
vals["modify_color_expression"] = self.modify_color_expression
|
||||
if self.kpi_id:
|
||||
vals.update(
|
||||
{
|
||||
|
@ -195,15 +189,19 @@ class KpiDashboardItem(models.Model):
|
|||
}
|
||||
)
|
||||
if self.kpi_id.compute_on_fly:
|
||||
vals.update({
|
||||
"value": self.kpi_id._compute_value(),
|
||||
"value_last_update": fields.Datetime.now(),
|
||||
})
|
||||
vals.update(
|
||||
{
|
||||
"value": self.kpi_id._compute_value(),
|
||||
"value_last_update": fields.Datetime.now(),
|
||||
}
|
||||
)
|
||||
else:
|
||||
vals.update({
|
||||
"value": self.kpi_id.value,
|
||||
"value_last_update": self.kpi_id.value_last_update,
|
||||
})
|
||||
vals.update(
|
||||
{
|
||||
"value": self.kpi_id.value,
|
||||
"value_last_update": self.kpi_id.value_last_update,
|
||||
}
|
||||
)
|
||||
if self.kpi_id.action_ids:
|
||||
vals["actions"] = self.kpi_id.action_ids.read_dashboard()
|
||||
else:
|
||||
|
@ -219,12 +217,13 @@ class KpiDashboardItem(models.Model):
|
|||
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,
|
||||
"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,
|
||||
}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
import ast
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.addons.base.models.ir_cron import _intervalTypes
|
||||
from odoo.tools.float_utils import float_compare
|
||||
import re
|
||||
import json
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
|
||||
from dateutil import relativedelta
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.float_utils import float_compare
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
from odoo.addons.base.models.ir_cron import _intervalTypes
|
||||
|
||||
|
||||
class KpiKpi(models.Model):
|
||||
_name = "kpi.kpi"
|
||||
|
@ -30,8 +33,13 @@ class KpiKpi(models.Model):
|
|||
args = fields.Char()
|
||||
kwargs = fields.Char()
|
||||
widget = fields.Selection(
|
||||
[('integer', 'Integer'), ("number", "Number"), ("meter", "Meter"),
|
||||
('counter', 'Counter'), ("graph", "Graph")],
|
||||
[
|
||||
("integer", "Integer"),
|
||||
("number", "Number"),
|
||||
("meter", "Meter"),
|
||||
("counter", "Counter"),
|
||||
("graph", "Graph"),
|
||||
],
|
||||
required=True,
|
||||
default="number",
|
||||
)
|
||||
|
@ -40,22 +48,21 @@ class KpiKpi(models.Model):
|
|||
suffix = fields.Char()
|
||||
action_ids = fields.One2many(
|
||||
"kpi.kpi.action",
|
||||
inverse_name='kpi_id',
|
||||
help="Actions that can be opened from the KPI"
|
||||
inverse_name="kpi_id",
|
||||
help="Actions that can be opened from the KPI",
|
||||
)
|
||||
code = fields.Text("Code")
|
||||
store_history = fields.Boolean()
|
||||
store_history_interval = fields.Selection(
|
||||
selection=lambda self:
|
||||
self.env['ir.cron']._fields['interval_type'].selection,
|
||||
selection=lambda self: self.env["ir.cron"]._fields["interval_type"].selection,
|
||||
)
|
||||
store_history_interval_number = fields.Integer()
|
||||
compute_on_fly = fields.Boolean()
|
||||
history_ids = fields.One2many("kpi.kpi.history", inverse_name="kpi_id")
|
||||
computed_value = fields.Serialized(compute='_compute_computed_value')
|
||||
computed_date = fields.Datetime(compute='_compute_computed_value')
|
||||
computed_value = fields.Serialized(compute="_compute_computed_value")
|
||||
computed_date = fields.Datetime(compute="_compute_computed_value")
|
||||
|
||||
@api.depends('value', 'value_last_update', 'compute_on_fly')
|
||||
@api.depends("value", "value_last_update", "compute_on_fly")
|
||||
def _compute_computed_value(self):
|
||||
for record in self:
|
||||
if record.compute_on_fly:
|
||||
|
@ -95,18 +102,19 @@ class KpiKpi(models.Model):
|
|||
value = self._compute_value()
|
||||
self.write({"value": value})
|
||||
if self.store_history:
|
||||
last = self.env['kpi.kpi.history'].search([
|
||||
('kpi_id', '=', self.id)
|
||||
], limit=1)
|
||||
last = self.env["kpi.kpi.history"].search(
|
||||
[("kpi_id", "=", self.id)], limit=1
|
||||
)
|
||||
if (
|
||||
not last or
|
||||
not self.store_history_interval or
|
||||
last.create_date + _intervalTypes[self.store_history_interval](
|
||||
self.store_history_interval_number) < fields.Datetime.now()
|
||||
):
|
||||
self.env["kpi.kpi.history"].create(
|
||||
self._generate_history_vals(value)
|
||||
not last
|
||||
or not self.store_history_interval
|
||||
or last.create_date
|
||||
+ _intervalTypes[self.store_history_interval](
|
||||
self.store_history_interval_number
|
||||
)
|
||||
< fields.Datetime.now()
|
||||
):
|
||||
self.env["kpi.kpi.history"].create(self._generate_history_vals(value))
|
||||
notifications = []
|
||||
for dashboard_item in self.dashboard_item_ids:
|
||||
channel = "kpi_dashboard_%s" % dashboard_item.dashboard_id.id
|
||||
|
@ -152,9 +160,9 @@ class KpiKpi(models.Model):
|
|||
if len(message) > 0:
|
||||
message += _(" or ")
|
||||
message += forbidden[-1]
|
||||
raise ValidationError(_(
|
||||
"The code cannot contain the following terms: %s."
|
||||
) % message)
|
||||
raise ValidationError(
|
||||
_("The code cannot contain the following terms: %s.") % message
|
||||
)
|
||||
results = self._get_code_input_dict()
|
||||
savepoint = "kpi_formula_%s" % self.id
|
||||
self.env.cr.execute("savepoint %s" % savepoint)
|
||||
|
@ -164,30 +172,34 @@ class KpiKpi(models.Model):
|
|||
|
||||
def show_value(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref('kpi_dashboard.kpi_kpi_act_window')
|
||||
action = self.env.ref("kpi_dashboard.kpi_kpi_act_window")
|
||||
result = action.read()[0]
|
||||
result.update({
|
||||
'res_id': self.id,
|
||||
'target': 'new',
|
||||
'view_mode': 'form',
|
||||
'views': [(self.env.ref(
|
||||
'kpi_dashboard.kpi_kpi_widget_form_view'
|
||||
).id, 'form')],
|
||||
})
|
||||
result.update(
|
||||
{
|
||||
"res_id": self.id,
|
||||
"target": "new",
|
||||
"view_mode": "form",
|
||||
"views": [
|
||||
(self.env.ref("kpi_dashboard.kpi_kpi_widget_form_view").id, "form")
|
||||
],
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class KpiKpiAction(models.Model):
|
||||
_name = 'kpi.kpi.action'
|
||||
_description = 'KPI action'
|
||||
_name = "kpi.kpi.action"
|
||||
_description = "KPI action"
|
||||
|
||||
kpi_id = fields.Many2one('kpi.kpi', required=True, ondelete='cascade')
|
||||
kpi_id = fields.Many2one("kpi.kpi", required=True, ondelete="cascade")
|
||||
action = fields.Reference(
|
||||
selection=[('ir.actions.report', 'ir.actions.report'),
|
||||
('ir.actions.act_window', 'ir.actions.act_window'),
|
||||
('ir.actions.act_url', 'ir.actions.act_url'),
|
||||
('ir.actions.server', 'ir.actions.server'),
|
||||
('ir.actions.client', 'ir.actions.client')],
|
||||
selection=[
|
||||
("ir.actions.report", "ir.actions.report"),
|
||||
("ir.actions.act_window", "ir.actions.act_window"),
|
||||
("ir.actions.act_url", "ir.actions.act_url"),
|
||||
("ir.actions.server", "ir.actions.server"),
|
||||
("ir.actions.client", "ir.actions.client"),
|
||||
],
|
||||
required=True,
|
||||
)
|
||||
context = fields.Char()
|
||||
|
@ -196,43 +208,45 @@ class KpiKpiAction(models.Model):
|
|||
result = {}
|
||||
for r in self:
|
||||
result[r.id] = {
|
||||
'id': r.action.id,
|
||||
'type': r.action._name,
|
||||
'name': r.action.name,
|
||||
'context': safe_eval(r.context or '{}')
|
||||
"id": r.action.id,
|
||||
"type": r.action._name,
|
||||
"name": r.action.name,
|
||||
"context": safe_eval(r.context or "{}"),
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
class KpiKpiHistory(models.Model):
|
||||
_name = 'kpi.kpi.history'
|
||||
_description = 'KPI history'
|
||||
_order = 'create_date DESC'
|
||||
_name = "kpi.kpi.history"
|
||||
_description = "KPI history"
|
||||
_order = "create_date DESC"
|
||||
|
||||
kpi_id = fields.Many2one(
|
||||
'kpi.kpi', required=True, ondelete='cascade', readonly=True
|
||||
"kpi.kpi", required=True, ondelete="cascade", readonly=True
|
||||
)
|
||||
value = fields.Serialized(readonly=True)
|
||||
raw_value = fields.Char(compute='_compute_raw_value')
|
||||
name = fields.Char(related='kpi_id.name')
|
||||
raw_value = fields.Char(compute="_compute_raw_value")
|
||||
name = fields.Char(related="kpi_id.name")
|
||||
widget = fields.Selection(
|
||||
selection=lambda self:
|
||||
self.env['kpi.kpi']._fields['widget'].selection,
|
||||
required=True)
|
||||
selection=lambda self: self.env["kpi.kpi"]._fields["widget"].selection,
|
||||
required=True,
|
||||
)
|
||||
|
||||
@api.depends('value')
|
||||
@api.depends("value")
|
||||
def _compute_raw_value(self):
|
||||
for record in self:
|
||||
record.raw_value = json.dumps(record.value)
|
||||
|
||||
def show_form(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref('kpi_dashboard.kpi_kpi_history_act_window')
|
||||
action = self.env.ref("kpi_dashboard.kpi_kpi_history_act_window")
|
||||
result = action.read()[0]
|
||||
result.update({
|
||||
'res_id': self.id,
|
||||
'target': 'new',
|
||||
'view_mode': 'form',
|
||||
'views': [(self.env.context.get('form_id'), 'form')],
|
||||
})
|
||||
result.update(
|
||||
{
|
||||
"res_id": self.id,
|
||||
"target": "new",
|
||||
"view_mode": "form",
|
||||
"views": [(self.env.context.get("form_id"), "form")],
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="group_kpi_dashboard_manager" model="res.groups">
|
||||
<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>
|
||||
<data noupdate="1">
|
||||
<record id="rule_kpi_dashboard" model="ir.rule">
|
||||
<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 id="rule_kpi_dashboard_all" model="ir.rule">
|
||||
<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="groups" eval="[(4, ref('group_kpi_dashboard_manager'))]"/>
|
||||
<field name="groups" eval="[(4, ref('group_kpi_dashboard_manager'))]" />
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
@ -1,150 +1,159 @@
|
|||
odoo.define('kpi_dashboard.DashboardController', function (require) {
|
||||
odoo.define("kpi_dashboard.DashboardController", function(require) {
|
||||
"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 _t = core._t;
|
||||
|
||||
var DashboardController = BasicController.extend({
|
||||
init: function () {
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.dashboard_context = {};
|
||||
this.dashboard_color_data = []
|
||||
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',
|
||||
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;
|
||||
this._rpc({
|
||||
model: this.modelName,
|
||||
method: 'read_dashboard_on_fly',
|
||||
method: "read_dashboard_on_fly",
|
||||
args: [[this.renderer.state.res_id]],
|
||||
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
|
||||
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, {
|
||||
validate: this.canBeDiscarded.bind(this),
|
||||
});
|
||||
this._super($node, options);
|
||||
},
|
||||
_pushState: function (state) {
|
||||
_pushState: function(state) {
|
||||
state = state || {};
|
||||
var env = this.model.get(this.handle, {env: true});
|
||||
state.id = env.currentId;
|
||||
this._super(state);
|
||||
},
|
||||
_addDashboard: function () {
|
||||
_addDashboard: function() {
|
||||
var self = this;
|
||||
var action = self.initialState.specialData.action_id;
|
||||
var name = self.initialState.specialData.name;
|
||||
if (! action) {
|
||||
if (!action) {
|
||||
self.do_warn(_t("First you must create the Menu"));
|
||||
}
|
||||
return self._rpc({
|
||||
route: '/board/add_to_dashboard',
|
||||
params: {
|
||||
action_id: action,
|
||||
context_to_save: {'res_id': self.initialState.res_id},
|
||||
domain: [('id', '=', self.initialState.res_id)],
|
||||
view_mode: 'dashboard',
|
||||
name: name,
|
||||
},
|
||||
})
|
||||
.then(function (r) {
|
||||
if (r) {
|
||||
self.do_notify(
|
||||
_.str.sprintf(_t("'%s' added to dashboard"), name),
|
||||
_t('Please refresh your browser for the changes to take effect.')
|
||||
);
|
||||
} else {
|
||||
self.do_warn(_t("Could not add KPI dashboard to dashboard"));
|
||||
}
|
||||
});
|
||||
return self
|
||||
._rpc({
|
||||
route: "/board/add_to_dashboard",
|
||||
params: {
|
||||
action_id: action,
|
||||
context_to_save: {res_id: self.initialState.res_id},
|
||||
domain: [("id", "=", self.initialState.res_id)],
|
||||
view_mode: "dashboard",
|
||||
name: name,
|
||||
},
|
||||
})
|
||||
.then(function(r) {
|
||||
if (r) {
|
||||
self.do_notify(
|
||||
_.str.sprintf(_t("'%s' added to dashboard"), name),
|
||||
_t(
|
||||
"Please refresh your browser for the changes to take effect."
|
||||
)
|
||||
);
|
||||
} else {
|
||||
self.do_warn(_t("Could not add KPI dashboard to dashboard"));
|
||||
}
|
||||
});
|
||||
},
|
||||
_updateButtons: function () {
|
||||
_updateButtons: function() {
|
||||
// HOOK Function
|
||||
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;
|
||||
}
|
||||
|
||||
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.$buttons.appendTo($node);
|
||||
},
|
||||
_getContext: function () {
|
||||
_getContext: function() {
|
||||
return _.extend(
|
||||
{},
|
||||
this.model.get(this.handle, {raw: true}).getContext(),
|
||||
{bin_size: true},
|
||||
this.dashboard_context,
|
||||
)
|
||||
this.dashboard_context
|
||||
);
|
||||
},
|
||||
_modifyContext: function (event) {
|
||||
_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
|
||||
)}),
|
||||
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) {
|
||||
_addModifyColor: function(event) {
|
||||
this.dashboard_color_data.push([
|
||||
event.data.element_id,
|
||||
event.data.expression,
|
||||
]);
|
||||
},
|
||||
_refreshColors: function () {
|
||||
_refreshColors: function() {
|
||||
var self = this;
|
||||
var ctx = this._getContext();
|
||||
_.each(this.dashboard_color_data, function (data) {
|
||||
_.each(this.dashboard_color_data, function(data) {
|
||||
var color = py.eval(data[1], {
|
||||
context: _.extend(ctx, {
|
||||
__getattr__: function() {return false},
|
||||
|
||||
__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);
|
||||
var $element = self.renderer.$el.find("#" + data[0]);
|
||||
$element.css("background-color", color);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return DashboardController;
|
||||
|
||||
});
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
odoo.define('kpi_dashboard.DashboardModel', function (require) {
|
||||
odoo.define("kpi_dashboard.DashboardModel", function(require) {
|
||||
"use strict";
|
||||
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var BasicModel = require("web.BasicModel");
|
||||
|
||||
var DashboardModel = BasicModel.extend({
|
||||
_fetchRecord: function (record, options) {
|
||||
_fetchRecord: function(record, options) {
|
||||
return this._rpc({
|
||||
model: record.model,
|
||||
method: 'read_dashboard',
|
||||
method: "read_dashboard",
|
||||
args: [[record.res_id]],
|
||||
context: _.extend({}, record.getContext(), {bin_size: true}),
|
||||
})
|
||||
.then(function (result) {
|
||||
}).then(function(result) {
|
||||
record.specialData = result;
|
||||
return result
|
||||
})
|
||||
}
|
||||
return result;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return DashboardModel;
|
||||
|
||||
});
|
||||
|
|
|
@ -1,99 +1,98 @@
|
|||
odoo.define('kpi_dashboard.DashboardRenderer', function (require) {
|
||||
odoo.define("kpi_dashboard.DashboardRenderer", function(require) {
|
||||
"use strict";
|
||||
|
||||
var BasicRenderer = require('web.BasicRenderer');
|
||||
var core = require('web.core');
|
||||
var registry = require('kpi_dashboard.widget_registry');
|
||||
var BusService = require('bus.BusService');
|
||||
var BasicRenderer = require("web.BasicRenderer");
|
||||
var core = require("web.core");
|
||||
var registry = require("kpi_dashboard.widget_registry");
|
||||
var BusService = require("bus.BusService");
|
||||
var qweb = core.qweb;
|
||||
|
||||
var DashboardRenderer= BasicRenderer.extend({
|
||||
var DashboardRenderer = BasicRenderer.extend({
|
||||
className: "o_dashboard_view",
|
||||
_getDashboardWidget: function (kpi) {
|
||||
var Widget = registry.getAny([
|
||||
kpi.widget, 'abstract',
|
||||
]);
|
||||
_getDashboardWidget: function(kpi) {
|
||||
var Widget = registry.getAny([kpi.widget, "abstract"]);
|
||||
var widget = new Widget(this, kpi);
|
||||
return widget;
|
||||
},
|
||||
_onClickModifyContext: function (modify_context_expression, event) {
|
||||
this.trigger_up('modify_context', {
|
||||
_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(
|
||||
'background-color', this.state.specialData.background_color);
|
||||
this.$el.find('.gridster')
|
||||
.css('width', this.state.specialData.width);
|
||||
this.$grid = this.$el.find('.gridster ul');
|
||||
_renderView: function() {
|
||||
this.$el.html($(qweb.render("dashboard_kpi.dashboard")));
|
||||
this.$el.css("background-color", this.state.specialData.background_color);
|
||||
this.$el.find(".gridster").css("width", this.state.specialData.width);
|
||||
this.$grid = this.$el.find(".gridster ul");
|
||||
var self = this;
|
||||
this.kpi_widget = {};
|
||||
_.each(this.state.specialData.item_ids, function (kpi) {
|
||||
var element = $(qweb.render(
|
||||
'kpi_dashboard.kpi', {widget: kpi}));
|
||||
element.css('background-color', kpi.color);
|
||||
element.css('color', kpi.font_color);
|
||||
element.attr('id', _.uniqueId('kpi_'));
|
||||
_.each(this.state.specialData.item_ids, function(kpi) {
|
||||
var element = $(qweb.render("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');
|
||||
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);
|
||||
});
|
||||
this.$grid.gridster({
|
||||
widget_margins: [
|
||||
this.state.specialData.margin_x,
|
||||
this.state.specialData.margin_y,
|
||||
],
|
||||
widget_base_dimensions: [
|
||||
this.state.specialData.widget_dimension_x,
|
||||
this.state.specialData.widget_dimension_y,
|
||||
],
|
||||
cols: this.state.specialData.max_cols,
|
||||
}).data('gridster').disable();
|
||||
this.channel = 'kpi_dashboard_' + this.state.res_id;
|
||||
this.call(
|
||||
'bus_service', 'addChannel', this.channel);
|
||||
this.call('bus_service', 'startPolling');
|
||||
this.call(
|
||||
'bus_service', 'onNotification',
|
||||
this, this._onNotification
|
||||
);
|
||||
this.$grid
|
||||
.gridster({
|
||||
widget_margins: [
|
||||
this.state.specialData.margin_x,
|
||||
this.state.specialData.margin_y,
|
||||
],
|
||||
widget_base_dimensions: [
|
||||
this.state.specialData.widget_dimension_x,
|
||||
this.state.specialData.widget_dimension_y,
|
||||
],
|
||||
cols: this.state.specialData.max_cols,
|
||||
})
|
||||
.data("gridster")
|
||||
.disable();
|
||||
this.channel = "kpi_dashboard_" + this.state.res_id;
|
||||
this.call("bus_service", "addChannel", this.channel);
|
||||
this.call("bus_service", "startPolling");
|
||||
this.call("bus_service", "onNotification", this, this._onNotification);
|
||||
if (this.state.specialData.compute_on_fly_refresh > 0) {
|
||||
// Setting the refresh interval
|
||||
this.on_fly_interval = setInterval(function () {
|
||||
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');
|
||||
this.on_fly_interval = setInterval(function() {
|
||||
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 () {
|
||||
on_detach_callback: function() {
|
||||
// We want to clear the refresh interval once we exit the view
|
||||
if (this.on_fly_interval) {
|
||||
clearInterval(this.on_fly_interval)
|
||||
clearInterval(this.on_fly_interval);
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
_onNotification: function (notifications) {
|
||||
_onNotification: function(notifications) {
|
||||
var self = this;
|
||||
_.each(notifications, function (notification) {
|
||||
_.each(notifications, function(notification) {
|
||||
var channel = notification[0];
|
||||
var message = notification[1];
|
||||
if (channel === self.channel && message) {
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
odoo.define('kpi_dashboard.DashboardView', function (require) {
|
||||
odoo.define("kpi_dashboard.DashboardView", function(require) {
|
||||
"use strict";
|
||||
|
||||
var BasicView = require('web.BasicView');
|
||||
var DashboardController = require('kpi_dashboard.DashboardController');
|
||||
var DashboardModel = require('kpi_dashboard.DashboardModel');
|
||||
var DashboardRenderer = require('kpi_dashboard.DashboardRenderer');
|
||||
var view_registry = require('web.view_registry');
|
||||
var core = require('web.core');
|
||||
var BasicView = require("web.BasicView");
|
||||
var DashboardController = require("kpi_dashboard.DashboardController");
|
||||
var DashboardModel = require("kpi_dashboard.DashboardModel");
|
||||
var DashboardRenderer = require("kpi_dashboard.DashboardRenderer");
|
||||
var view_registry = require("web.view_registry");
|
||||
var core = require("web.core");
|
||||
|
||||
var _lt = core._lt;
|
||||
|
||||
var DashboardView = BasicView.extend({
|
||||
jsLibs: [
|
||||
'/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.js',
|
||||
],
|
||||
cssLibs: [
|
||||
'/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.css',
|
||||
],
|
||||
jsLibs: ["/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.js"],
|
||||
cssLibs: ["/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.css"],
|
||||
accesskey: "d",
|
||||
display_name: _lt("Dashboard"),
|
||||
icon: 'fa-tachometer',
|
||||
viewType: 'dashboard',
|
||||
icon: "fa-tachometer",
|
||||
viewType: "dashboard",
|
||||
config: _.extend({}, BasicView.prototype.config, {
|
||||
Controller: DashboardController,
|
||||
Renderer: DashboardRenderer,
|
||||
|
@ -28,17 +24,17 @@ odoo.define('kpi_dashboard.DashboardView', function (require) {
|
|||
}),
|
||||
multi_record: false,
|
||||
searchable: false,
|
||||
init: function () {
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.controllerParams.mode = 'readonly';
|
||||
this.loadParams.type = 'record';
|
||||
if (! this.loadParams.res_id && this.loadParams.context.res_id) {
|
||||
this.controllerParams.mode = "readonly";
|
||||
this.loadParams.type = "record";
|
||||
if (!this.loadParams.res_id && this.loadParams.context.res_id) {
|
||||
this.loadParams.res_id = this.loadParams.context.res_id;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
view_registry.add('dashboard', DashboardView);
|
||||
view_registry.add("dashboard", DashboardView);
|
||||
|
||||
return DashboardView;
|
||||
});
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
odoo.define('kpi_dashboard.KpiFieldWidget', function(require) {
|
||||
odoo.define("kpi_dashboard.KpiFieldWidget", function(require) {
|
||||
"use strict";
|
||||
|
||||
var basic_fields = require('web.basic_fields');
|
||||
var field_registry = require('web.field_registry');
|
||||
var core = require('web.core');
|
||||
var basic_fields = require("web.basic_fields");
|
||||
var field_registry = require("web.field_registry");
|
||||
var core = require("web.core");
|
||||
var qweb = core.qweb;
|
||||
var registry = require('kpi_dashboard.widget_registry');
|
||||
var registry = require("kpi_dashboard.widget_registry");
|
||||
|
||||
var KpiFieldWidget = basic_fields.FieldChar.extend({
|
||||
jsLibs: [
|
||||
'/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.js',
|
||||
],
|
||||
cssLibs: [
|
||||
'/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.css',
|
||||
],
|
||||
className: 'o_dashboard_view',
|
||||
_renderReadonly: function () {
|
||||
this.$el.html($(qweb.render('dashboard_kpi.dashboard')));
|
||||
jsLibs: ["/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.js"],
|
||||
cssLibs: ["/kpi_dashboard/static/lib/gridster/jquery.dsmorse-gridster.min.css"],
|
||||
className: "o_dashboard_view",
|
||||
_renderReadonly: function() {
|
||||
this.$el.html($(qweb.render("dashboard_kpi.dashboard")));
|
||||
var marginx = 0;
|
||||
var marginy = 0;
|
||||
var widgetx = 400;
|
||||
var widgety = 400;
|
||||
this.$el.find('.gridster').css('width', widgety);
|
||||
this.$grid = this.$el.find('.gridster ul');
|
||||
this.$el.find(".gridster").css("width", widgety);
|
||||
this.$grid = this.$el.find(".gridster ul");
|
||||
var widgetVals = {
|
||||
value: this.value,
|
||||
col: 1,
|
||||
|
@ -30,10 +26,11 @@ odoo.define('kpi_dashboard.KpiFieldWidget', function(require) {
|
|||
sizex: 1,
|
||||
sizey: 1,
|
||||
name: this.recordData[this.nodeOptions.name],
|
||||
value_last_update: this.recordData[this.nodeOptions.date]
|
||||
}
|
||||
value_last_update: this.recordData[this.nodeOptions.date],
|
||||
};
|
||||
var Widget = registry.getAny([
|
||||
this.recordData[this.nodeOptions.widget], 'abstract',
|
||||
this.recordData[this.nodeOptions.widget],
|
||||
"abstract",
|
||||
]);
|
||||
this.state = {
|
||||
specialData: {
|
||||
|
@ -41,28 +38,24 @@ odoo.define('kpi_dashboard.KpiFieldWidget', function(require) {
|
|||
margin_y: marginy,
|
||||
widget_dimension_x: widgetx,
|
||||
widget_dimension_y: widgety,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
var widget = new Widget(this, widgetVals);
|
||||
var element = $(qweb.render(
|
||||
'kpi_dashboard.kpi', {widget: widgetVals}));
|
||||
element.css('background-color', 'white');
|
||||
element.css('color', 'black');
|
||||
var element = $(qweb.render("kpi_dashboard.kpi", {widget: widgetVals}));
|
||||
element.css("background-color", "white");
|
||||
element.css("color", "black");
|
||||
this.$grid.append(element);
|
||||
widget.appendTo(element)
|
||||
this.$grid.gridster({
|
||||
widget_margins: [
|
||||
marginx,
|
||||
marginy,
|
||||
],
|
||||
widget_base_dimensions: [
|
||||
widgetx,
|
||||
widgety,
|
||||
],
|
||||
cols: 1,
|
||||
}).data('gridster').disable();
|
||||
widget.appendTo(element);
|
||||
this.$grid
|
||||
.gridster({
|
||||
widget_margins: [marginx, marginy],
|
||||
widget_base_dimensions: [widgetx, widgety],
|
||||
cols: 1,
|
||||
})
|
||||
.data("gridster")
|
||||
.disable();
|
||||
},
|
||||
});
|
||||
field_registry.add('kpi', KpiFieldWidget);
|
||||
field_registry.add("kpi", KpiFieldWidget);
|
||||
return KpiFieldWidget;
|
||||
});
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
odoo.define('kpi_dashboard.AbstractWidget', function (require) {
|
||||
odoo.define("kpi_dashboard.AbstractWidget", function(require) {
|
||||
"use strict";
|
||||
var Widget = require('web.Widget');
|
||||
var field_utils = require('web.field_utils');
|
||||
var time = require('web.time');
|
||||
var ajax = require('web.ajax');
|
||||
var registry = require('kpi_dashboard.widget_registry');
|
||||
var Widget = require("web.Widget");
|
||||
var field_utils = require("web.field_utils");
|
||||
var time = require("web.time");
|
||||
var ajax = require("web.ajax");
|
||||
var registry = require("kpi_dashboard.widget_registry");
|
||||
|
||||
var AbstractWidget = Widget.extend({
|
||||
template: 'kpi_dashboard.base_widget', // Template used by the widget
|
||||
template: "kpi_dashboard.base_widget", // Template used by the widget
|
||||
cssLibs: [], // Specific css of the widget
|
||||
jsLibs: [], // Specific Javascript libraries of the widget
|
||||
events: {
|
||||
'click .o_kpi_dashboard_toggle_button': '_onClickToggleButton',
|
||||
'click .direct_action': '_onClickDirectAction',
|
||||
"click .o_kpi_dashboard_toggle_button": "_onClickToggleButton",
|
||||
"click .direct_action": "_onClickDirectAction",
|
||||
},
|
||||
init: function (parent, kpi_values) {
|
||||
init: function(parent, kpi_values) {
|
||||
this._super(parent);
|
||||
this.col = kpi_values.col;
|
||||
this.row = kpi_values.row;
|
||||
|
@ -29,66 +29,70 @@ odoo.define('kpi_dashboard.AbstractWidget', function (require) {
|
|||
this.prefix = kpi_values.prefix;
|
||||
this.suffix = kpi_values.suffix;
|
||||
this.actions = kpi_values.actions;
|
||||
this.widget_size_x = this.widget_dimension_x * this.sizex +
|
||||
(this.sizex - 1) * this.margin_x;
|
||||
this.widget_size_y = this.widget_dimension_y * this.sizey +
|
||||
(this.sizey - 1) * this.margin_y;
|
||||
this.widget_size_x =
|
||||
this.widget_dimension_x * this.sizex + (this.sizex - 1) * this.margin_x;
|
||||
this.widget_size_y =
|
||||
this.widget_dimension_y * this.sizey + (this.sizey - 1) * this.margin_y;
|
||||
},
|
||||
willStart: function () {
|
||||
willStart: function() {
|
||||
// We need to load the libraries before the start
|
||||
return $.when(ajax.loadLibs(this), this._super.apply(this, arguments));
|
||||
},
|
||||
start: function () {
|
||||
start: function() {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
return this._super.apply(this, arguments).then(function() {
|
||||
self._fillWidget(self.values);
|
||||
});
|
||||
},
|
||||
_onClickToggleButton: function (event) {
|
||||
_onClickToggleButton: function(event) {
|
||||
event.preventDefault();
|
||||
this.$el.toggleClass('o_dropdown_open');
|
||||
this.$el.toggleClass("o_dropdown_open");
|
||||
},
|
||||
_fillWidget: function (values) {
|
||||
_fillWidget: function(values) {
|
||||
// This function fills the widget values
|
||||
if (this.$el === undefined)
|
||||
return;
|
||||
if (this.$el === undefined) return;
|
||||
this.fillWidget(values);
|
||||
var item = this.$el.find('[data-bind="value_last_update_display"]');
|
||||
if (item && ! values.compute_on_fly && values.value_last_update !== undefined) {
|
||||
if (
|
||||
item &&
|
||||
!values.compute_on_fly &&
|
||||
values.value_last_update !== undefined
|
||||
) {
|
||||
var value = field_utils.parse.datetime(values.value_last_update);
|
||||
item.text(value.clone().add(
|
||||
this.getSession().getTZOffset(value), 'minutes').format(
|
||||
time.getLangDatetimeFormat()
|
||||
));
|
||||
item.text(
|
||||
value
|
||||
.clone()
|
||||
.add(this.getSession().getTZOffset(value), "minutes")
|
||||
.format(time.getLangDatetimeFormat())
|
||||
);
|
||||
}
|
||||
var $manage = this.$el.find('.o_kpi_dashboard_manage');
|
||||
var $manage = this.$el.find(".o_kpi_dashboard_manage");
|
||||
if ($manage && this.showManagePanel(values))
|
||||
$manage.toggleClass('hidden', false);
|
||||
$manage.toggleClass("hidden", false);
|
||||
},
|
||||
showManagePanel: function (values) {
|
||||
showManagePanel: function(values) {
|
||||
// Hook for extensions
|
||||
return (values.actions !== undefined);
|
||||
return values.actions !== undefined;
|
||||
},
|
||||
fillWidget: function (values) {
|
||||
fillWidget: function(values) {
|
||||
// Specific function that will be changed by specific widget
|
||||
var value = values.value;
|
||||
var self = this;
|
||||
_.each(value, function (val, key) {
|
||||
var item = self.$el.find('[data-bind=' + key + ']')
|
||||
if (item)
|
||||
item.text(val);
|
||||
})
|
||||
_.each(value, function(val, key) {
|
||||
var item = self.$el.find("[data-bind=" + key + "]");
|
||||
if (item) item.text(val);
|
||||
});
|
||||
},
|
||||
_onClickDirectAction: function(event) {
|
||||
event.preventDefault();
|
||||
var $data = $(event.currentTarget).closest('a');
|
||||
var action = this.actions[$($data).data('id')];
|
||||
var $data = $(event.currentTarget).closest("a");
|
||||
var action = this.actions[$($data).data("id")];
|
||||
return this.do_action(action.id, {
|
||||
additional_context: action.context
|
||||
additional_context: action.context,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
registry.add('abstract', AbstractWidget);
|
||||
registry.add("abstract", AbstractWidget);
|
||||
return AbstractWidget;
|
||||
});
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
odoo.define('kpi_dashboard.CounterWidget', function (require) {
|
||||
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 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);
|
||||
registry.add("counter", CounterWidget);
|
||||
return CounterWidget;
|
||||
});
|
||||
|
|
|
@ -1,40 +1,39 @@
|
|||
odoo.define('kpi_dashboard.GraphWidget', function (require) {
|
||||
odoo.define("kpi_dashboard.GraphWidget", function(require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractWidget = require('kpi_dashboard.AbstractWidget');
|
||||
var registry = require('kpi_dashboard.widget_registry');
|
||||
var core = require('web.core');
|
||||
var AbstractWidget = require("kpi_dashboard.AbstractWidget");
|
||||
var registry = require("kpi_dashboard.widget_registry");
|
||||
var core = require("web.core");
|
||||
var qweb = core.qweb;
|
||||
|
||||
|
||||
var GraphWidget = AbstractWidget.extend({
|
||||
template: 'kpi_dashboard.graph',
|
||||
template: "kpi_dashboard.graph",
|
||||
jsLibs: [
|
||||
'/web/static/lib/nvd3/d3.v3.js',
|
||||
'/web/static/lib/nvd3/nv.d3.js',
|
||||
'/web/static/src/js/libs/nvd3.js',
|
||||
"/web/static/lib/nvd3/d3.v3.js",
|
||||
"/web/static/lib/nvd3/nv.d3.js",
|
||||
"/web/static/src/js/libs/nvd3.js",
|
||||
],
|
||||
cssLibs: [
|
||||
'/web/static/lib/nvd3/nv.d3.css',
|
||||
],
|
||||
start: function () {
|
||||
cssLibs: ["/web/static/lib/nvd3/nv.d3.css"],
|
||||
start: function() {
|
||||
this._onResize = this._onResize.bind(this);
|
||||
nv.utils.windowResize(this._onResize);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
if ('nv' in window && nv.utils && nv.utils.offWindowResize) {
|
||||
// if the widget is destroyed before the lazy loaded libs (nv) are
|
||||
destroy: function() {
|
||||
if ("nv" in window && nv.utils && nv.utils.offWindowResize) {
|
||||
// If the widget is destroyed before the lazy loaded libs (nv) are
|
||||
// actually loaded (i.e. after the widget has actually started),
|
||||
// nv is undefined, but the handler isn't bound yet anyway
|
||||
nv.utils.offWindowResize(this._onResize);
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
_getChartOptions: function (values) {
|
||||
_getChartOptions: function(values) {
|
||||
return {
|
||||
x: function (d, u) { return u; },
|
||||
margin: {'left': 0, 'right': 0, 'top': 5, 'bottom': 0},
|
||||
x: function(d, u) {
|
||||
return u;
|
||||
},
|
||||
margin: {left: 0, right: 0, top: 5, bottom: 0},
|
||||
showYAxis: false,
|
||||
showXAxis: false,
|
||||
showLegend: false,
|
||||
|
@ -42,60 +41,58 @@ odoo.define('kpi_dashboard.GraphWidget', function (require) {
|
|||
width: this.widget_size_x - 20,
|
||||
};
|
||||
},
|
||||
_chartConfiguration: function (values) {
|
||||
|
||||
_chartConfiguration: function(values) {
|
||||
this.chart.forceY([0]);
|
||||
this.chart.xAxis.tickFormat(function (d) {
|
||||
var label = '';
|
||||
_.each(values.value.graphs, function (v) {
|
||||
this.chart.xAxis.tickFormat(function(d) {
|
||||
var label = "";
|
||||
_.each(values.value.graphs, function(v) {
|
||||
if (v.values[d] && v.values[d].x) {
|
||||
label = v.values[d].x;
|
||||
}
|
||||
});
|
||||
return label;
|
||||
});
|
||||
this.chart.yAxis.tickFormat(d3.format(',.2f'));
|
||||
this.chart.yAxis.tickFormat(d3.format(",.2f"));
|
||||
|
||||
this.chart.tooltip.contentGenerator(function (key) {
|
||||
return qweb.render('GraphCustomTooltip', {
|
||||
'color': key.point.color,
|
||||
'key': key.series[0].title,
|
||||
'value': d3.format(',.2f')(key.point.y)
|
||||
this.chart.tooltip.contentGenerator(function(key) {
|
||||
return qweb.render("GraphCustomTooltip", {
|
||||
color: key.point.color,
|
||||
key: key.series[0].title,
|
||||
value: d3.format(",.2f")(key.point.y),
|
||||
});
|
||||
});
|
||||
},
|
||||
_addGraph: function (values) {
|
||||
_addGraph: function(values) {
|
||||
var data = values.value.graphs;
|
||||
this.$svg.addClass('o_graph_linechart');
|
||||
this.$svg.addClass("o_graph_linechart");
|
||||
this.chart = nv.models.lineChart();
|
||||
this.chart.options(
|
||||
this._getChartOptions(values)
|
||||
);
|
||||
this.chart.options(this._getChartOptions(values));
|
||||
this._chartConfiguration(values);
|
||||
d3.select(this.$('svg')[0])
|
||||
d3.select(this.$("svg")[0])
|
||||
.datum(data)
|
||||
.transition().duration(600)
|
||||
.transition()
|
||||
.duration(600)
|
||||
.call(this.chart);
|
||||
this.$('svg').css('height', this.widget_size_y - 90);
|
||||
this.$("svg").css("height", this.widget_size_y - 90);
|
||||
this._customizeChart();
|
||||
},
|
||||
fillWidget: function (values) {
|
||||
fillWidget: function(values) {
|
||||
var self = this;
|
||||
var element = this.$el.find('[data-bind="value"]');
|
||||
element.empty();
|
||||
element.css('padding-left', 10).css('padding-right', 10);
|
||||
element.css("padding-left", 10).css("padding-right", 10);
|
||||
this.chart = null;
|
||||
nv.addGraph(function () {
|
||||
self.$svg = self.$el.find(
|
||||
'[data-bind="value"]'
|
||||
).append('<svg width=' + (self.widget_size_x - 20) + '>');
|
||||
nv.addGraph(function() {
|
||||
self.$svg = self.$el
|
||||
.find('[data-bind="value"]')
|
||||
.append("<svg width=" + (self.widget_size_x - 20) + ">");
|
||||
self._addGraph(values);
|
||||
});
|
||||
},
|
||||
_customizeChart: function () {
|
||||
_customizeChart: function() {
|
||||
// Hook function
|
||||
},
|
||||
_onResize: function () {
|
||||
_onResize: function() {
|
||||
if (this.chart) {
|
||||
this.chart.update();
|
||||
this._customizeChart();
|
||||
|
@ -103,6 +100,6 @@ odoo.define('kpi_dashboard.GraphWidget', function (require) {
|
|||
},
|
||||
});
|
||||
|
||||
registry.add('graph', GraphWidget);
|
||||
registry.add("graph", GraphWidget);
|
||||
return GraphWidget;
|
||||
});
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
odoo.define('kpi_dashboard.IntegerWidget', function (require) {
|
||||
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 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',
|
||||
template: "kpi_dashboard.number",
|
||||
digits: [3, 0],
|
||||
shortList: [
|
||||
[1000000000000, 'T', [3, 1]],
|
||||
[1000000000, 'G', [3, 1]],
|
||||
[1000000, 'M', [3, 1]],
|
||||
[1000, 'K', [3, 1]]
|
||||
[1000000000000, "T", [3, 1]],
|
||||
[1000000000, "G", [3, 1]],
|
||||
[1000000, "M", [3, 1]],
|
||||
[1000, "K", [3, 1]],
|
||||
],
|
||||
shortNumber: function (num) {
|
||||
var suffix = '';
|
||||
shortNumber: function(num) {
|
||||
var suffix = "";
|
||||
var shortened = false;
|
||||
var digits = this.digits;
|
||||
_.each(this.shortList, function (shortItem) {
|
||||
_.each(this.shortList, function(shortItem) {
|
||||
if (!shortened && Math.abs(num) >= shortItem[0]) {
|
||||
shortened = true;
|
||||
suffix = shortItem[1];
|
||||
num = num / shortItem[0];
|
||||
num /= shortItem[0];
|
||||
digits = shortItem[2];
|
||||
}
|
||||
});
|
||||
return field_utils.format.float(num, false, {
|
||||
digits: digits}) + suffix;
|
||||
return (
|
||||
field_utils.format.float(num, false, {
|
||||
digits: digits,
|
||||
}) + suffix
|
||||
);
|
||||
},
|
||||
fillWidget: function (values) {
|
||||
fillWidget: function(values) {
|
||||
var widget = this.$el;
|
||||
var value = values.value.value;
|
||||
if (value === undefined) {
|
||||
|
@ -42,30 +44,31 @@ odoo.define('kpi_dashboard.IntegerWidget', function (require) {
|
|||
}
|
||||
var previous = values.value.previous;
|
||||
|
||||
var $change_rate = widget.find('.change-rate');
|
||||
var $change_rate = widget.find(".change-rate");
|
||||
if (previous === undefined) {
|
||||
$change_rate.toggleClass('active', false);
|
||||
$change_rate.toggleClass("active", false);
|
||||
} else {
|
||||
var difference = 0;
|
||||
if (previous !== 0) {
|
||||
difference = field_utils.format.integer(
|
||||
(100 * value / previous) - 100) + '%';
|
||||
difference =
|
||||
field_utils.format.integer((100 * value) / previous - 100) +
|
||||
"%";
|
||||
}
|
||||
$change_rate.toggleClass('active', true);
|
||||
$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);
|
||||
$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);
|
||||
$arrow.toggleClass("fa-arrow-up", true);
|
||||
$arrow.toggleClass("fa-arrow-down", false);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
registry.add('integer', IntegerWidget);
|
||||
registry.add("integer", IntegerWidget);
|
||||
return IntegerWidget;
|
||||
});
|
||||
|
|
|
@ -1,39 +1,34 @@
|
|||
odoo.define('kpi_dashboard.MeterWidget', function (require) {
|
||||
odoo.define("kpi_dashboard.MeterWidget", function(require) {
|
||||
"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({
|
||||
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 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.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 {
|
||||
percent: values.value.value,
|
||||
style: 'Arch',
|
||||
style: "Arch",
|
||||
width: 10,
|
||||
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,
|
||||
animate_text_colors: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
registry.add('meter', MeterWidget);
|
||||
registry.add("meter", MeterWidget);
|
||||
return MeterWidget;
|
||||
});
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
odoo.define('kpi_dashboard.NumberWidget', function (require) {
|
||||
odoo.define("kpi_dashboard.NumberWidget", 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 IntegerWidget = require("kpi_dashboard.IntegerWidget");
|
||||
var registry = require("kpi_dashboard.widget_registry");
|
||||
var field_utils = require("web.field_utils");
|
||||
|
||||
var NumberWidget = IntegerWidget.extend({
|
||||
digits: [3, 1],
|
||||
shortNumber: function (num) {
|
||||
shortNumber: function(num) {
|
||||
if (Math.abs(num) < 10) {
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
odoo.define('kpi_dashboard.TextWidget', function (require) {
|
||||
odoo.define("kpi_dashboard.TextWidget", function(require) {
|
||||
"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({
|
||||
template: 'kpi_dashboard.base_text',
|
||||
fillWidget: function () {
|
||||
template: "kpi_dashboard.base_text",
|
||||
fillWidget: function() {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
registry.add('base_text', TextWidget);
|
||||
registry.add("base_text", 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";
|
||||
|
||||
var Registry = require('web.Registry');
|
||||
var Registry = require("web.Registry");
|
||||
|
||||
return new Registry();
|
||||
});
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
.o_dashboard_view {
|
||||
height: 100%;
|
||||
@include o-webclient-padding($top: $o-horizontal-padding/2, $bottom: $o-horizontal-padding/2);
|
||||
@include o-webclient-padding(
|
||||
$top: $o-horizontal-padding/2,
|
||||
$bottom: $o-horizontal-padding/2
|
||||
);
|
||||
display: flex;
|
||||
>.gridster {
|
||||
> .gridster {
|
||||
margin: 0 auto;
|
||||
>ul {
|
||||
>li {
|
||||
> ul {
|
||||
> li {
|
||||
text-align: center;
|
||||
list-style: none outside none;
|
||||
}
|
||||
|
@ -56,7 +59,7 @@
|
|||
max-width: 400px;
|
||||
}
|
||||
.o_kpi_dashboard_manage_section {
|
||||
border-bottom: 1px solid gray('300');
|
||||
border-bottom: 1px solid gray("300");
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
> div {
|
||||
|
@ -71,7 +74,7 @@
|
|||
}
|
||||
.o_kpi_dashboard_toggle_button {
|
||||
background: white;
|
||||
border-color: gray('400');
|
||||
border-color: gray("400");
|
||||
z-index: $zindex-dropdown + 1;
|
||||
}
|
||||
}
|
||||
|
@ -82,8 +85,9 @@
|
|||
right: 0;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
span, b{
|
||||
margin: 0 23%;
|
||||
span,
|
||||
b {
|
||||
margin: 0 23%;
|
||||
width: 54%;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
|
@ -93,20 +97,20 @@
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
[data-style="Semi"] B{
|
||||
Margin: 0 10%;
|
||||
Width: 80%;
|
||||
[data-style="Semi"] b {
|
||||
margin: 0 10%;
|
||||
width: 80%;
|
||||
}
|
||||
S, U{
|
||||
Text-Decoration:None;
|
||||
s,
|
||||
u {
|
||||
text-decoration: None;
|
||||
font-height: 100;
|
||||
}
|
||||
B{
|
||||
Color: Black;
|
||||
Font-Weight: 200;
|
||||
Font-Size: 0.85em;
|
||||
Opacity: .8;
|
||||
b {
|
||||
color: Black;
|
||||
font-weight: 200;
|
||||
font-size: 0.85em;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
<template>
|
||||
<t t-name="dashboard_kpi.dashboard">
|
||||
<div class="gridster kpi_dashboard">
|
||||
<ul/>
|
||||
<ul />
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.kpi">
|
||||
<li t-att-data-row="widget.row"
|
||||
<li
|
||||
t-att-data-row="widget.row"
|
||||
t-att-data-col="widget.col"
|
||||
t-att-data-sizex="widget.sizex"
|
||||
t-att-data-sizey="widget.sizey"
|
||||
|
@ -14,15 +15,21 @@
|
|||
</t>
|
||||
<t t-name="kpi_dashboard.base_text">
|
||||
<div class="kpi">
|
||||
<h1 class="title" t-esc="widget.values.name"/>
|
||||
<h1 class="title" t-esc="widget.values.name" />
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.ManagePanel">
|
||||
<t t-if="widget.actions" >
|
||||
<t t-if="widget.actions">
|
||||
<t t-foreach="widget.actions" t-as="action_id">
|
||||
<t t-set="action" t-value="widget.actions[action_id]"/>
|
||||
<t t-set="action" t-value="widget.actions[action_id]" />
|
||||
<div role="menuitem" class="">
|
||||
<a role="menuitem" href="#" class="direct_action" t-att-data-id="action_id" t-att-data-type="action.type">Go to <t t-esc="action.name"/></a>
|
||||
<a
|
||||
role="menuitem"
|
||||
href="#"
|
||||
class="direct_action"
|
||||
t-att-data-id="action_id"
|
||||
t-att-data-type="action.type"
|
||||
>Go to <t t-esc="action.name" /></a>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
@ -31,45 +38,55 @@
|
|||
<div class="kpi">
|
||||
<div class="o_kpi_dashboard_manage hidden">
|
||||
<a class="o_kpi_dashboard_toggle_button" href="#">
|
||||
<i class="fa fa-ellipsis-v" aria-label="Selection" role="img" title="Selection"/>
|
||||
<i
|
||||
class="fa fa-ellipsis-v"
|
||||
aria-label="Selection"
|
||||
role="img"
|
||||
title="Selection"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="title" t-esc="widget.values.name"/>
|
||||
<p class="updated_at" data-bind="value_last_update_display"/>
|
||||
<h1 class="title" t-esc="widget.values.name" />
|
||||
<p class="updated_at" data-bind="value_last_update_display" />
|
||||
<div class="container o_kpi_dashboard_manage_panel dropdown-menu">
|
||||
<t t-call="kpi_dashboard.ManagePanel"/>
|
||||
<t t-call="kpi_dashboard.ManagePanel" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.number" t-extend="kpi_dashboard.base_widget">
|
||||
<t t-jquery="h1" t-operation="after">
|
||||
<h2 class="numbervalue">
|
||||
<span t-esc="widget.prefix"/><span data-bind="value"/><span t-esc="widget.suffix"/>
|
||||
<span t-esc="widget.prefix" />
|
||||
<span data-bind="value" />
|
||||
<span t-esc="widget.suffix" />
|
||||
</h2>
|
||||
<p class="change-rate">
|
||||
<i class="fa" data-bind="arrow"/>
|
||||
<span data-bind="difference"/>
|
||||
<i class="fa" data-bind="arrow" />
|
||||
<span data-bind="difference" />
|
||||
</p>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.meter" t-extend="kpi_dashboard.base_widget">
|
||||
<t t-jquery="h1" t-operation="after">
|
||||
<div class="centered">
|
||||
<div class="GaugeMeter" data-bind="value"/>
|
||||
<div class="GaugeMeter" data-bind="value" />
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.graph" t-extend="kpi_dashboard.base_widget">
|
||||
<t t-jquery="h1" t-operation="after">
|
||||
<div class="centered">
|
||||
<div data-bind="value"/>
|
||||
<div data-bind="value" />
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="kpi_dashboard.buttons">
|
||||
<div class="o_dashboard_buttons" role="toolbar" aria-label="Main actions">
|
||||
<button type="button"
|
||||
class="btn btn-primary o_dashboard_button_add" accesskey="d">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary o_dashboard_button_add"
|
||||
accesskey="d"
|
||||
>
|
||||
Add to Dashboard
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,28 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_backend"
|
||||
name="Backend Assets (used in backend interface)"
|
||||
inherit_id="web.assets_backend">
|
||||
<template
|
||||
id="assets_backend"
|
||||
name="Backend Assets (used in backend interface)"
|
||||
inherit_id="web.assets_backend"
|
||||
>
|
||||
<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>
|
||||
</template>
|
||||
</odoo>
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestFormula(TransactionCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.kpi = self.env["kpi.kpi"].create(
|
||||
{
|
||||
"name": "DEMO KPI",
|
||||
"widget": "number",
|
||||
"computation_method": "code",
|
||||
}
|
||||
{"name": "DEMO KPI", "widget": "number", "computation_method": "code",}
|
||||
)
|
||||
|
||||
def test_forbidden_words_01(self):
|
||||
|
@ -45,10 +41,10 @@ self.env.cr.execute("CoMMiT")
|
|||
self.kpi.compute()
|
||||
self.assertEqual(self.kpi.value, {})
|
||||
self.kpi.code = """
|
||||
result = {}
|
||||
result['value'] = len(model.search([('id', '=', %s)]))
|
||||
result['previous'] = len(model.search([('id', '!=', %s)]))
|
||||
""" % (
|
||||
result = {{}}
|
||||
result['value'] = len(model.search([('id', '=', {})]))
|
||||
result['previous'] = len(model.search([('id', '!=', {})]))
|
||||
""".format(
|
||||
self.kpi.id,
|
||||
self.kpi.id,
|
||||
)
|
||||
|
@ -66,10 +62,10 @@ result['previous'] = len(model.search([('id', '!=', %s)]))
|
|||
self.assertTrue(self.kpi.history_ids)
|
||||
self.assertEqual(self.kpi.value, {})
|
||||
self.kpi.code = """
|
||||
result = {}
|
||||
result['value'] = len(model.search([('id', '=', %s)]))
|
||||
result['previous'] = len(model.search([('id', '!=', %s)]))
|
||||
""" % (
|
||||
result = {{}}
|
||||
result['value'] = len(model.search([('id', '=', {})]))
|
||||
result['previous'] = len(model.search([('id', '!=', {})]))
|
||||
""".format(
|
||||
self.kpi.id,
|
||||
self.kpi.id,
|
||||
)
|
||||
|
|
|
@ -1,85 +1,92 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import Form
|
||||
from mock import patch
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import Form, TransactionCase
|
||||
|
||||
|
||||
class TestKpiDashboard(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestKpiDashboard, self).setUp()
|
||||
self.kpi_01 = self.env['kpi.kpi'].create({
|
||||
'name': 'KPI 01',
|
||||
'computation_method': 'function',
|
||||
'widget': 'number',
|
||||
'function': 'test_demo_number'
|
||||
})
|
||||
self.kpi_02 = self.env['kpi.kpi'].create({
|
||||
'name': 'KPI 02',
|
||||
'computation_method': 'function',
|
||||
'widget': 'number',
|
||||
'function': 'test_demo_number'
|
||||
})
|
||||
self.dashboard = self.env['kpi.dashboard'].create({
|
||||
'name': 'Dashboard',
|
||||
'number_of_columns': 4,
|
||||
'widget_dimension_x': 250,
|
||||
'widget_dimension_y': 250,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'name': self.kpi_01.name,
|
||||
'row': 1,
|
||||
'column': 1,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'name': self.kpi_02.name,
|
||||
'kpi_id': self.kpi_02.id,
|
||||
'row': 1,
|
||||
'column': 2,
|
||||
})
|
||||
self.env['kpi.dashboard.item'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'name': 'TITLE',
|
||||
'row': 2,
|
||||
'column': 1,
|
||||
})
|
||||
self.kpi_01 = self.env["kpi.kpi"].create(
|
||||
{
|
||||
"name": "KPI 01",
|
||||
"computation_method": "function",
|
||||
"widget": "number",
|
||||
"function": "test_demo_number",
|
||||
}
|
||||
)
|
||||
self.kpi_02 = self.env["kpi.kpi"].create(
|
||||
{
|
||||
"name": "KPI 02",
|
||||
"computation_method": "function",
|
||||
"widget": "number",
|
||||
"function": "test_demo_number",
|
||||
}
|
||||
)
|
||||
self.dashboard = self.env["kpi.dashboard"].create(
|
||||
{
|
||||
"name": "Dashboard",
|
||||
"number_of_columns": 4,
|
||||
"widget_dimension_x": 250,
|
||||
"widget_dimension_y": 250,
|
||||
}
|
||||
)
|
||||
self.env["kpi.dashboard.item"].create(
|
||||
{
|
||||
"dashboard_id": self.dashboard.id,
|
||||
"kpi_id": self.kpi_01.id,
|
||||
"name": self.kpi_01.name,
|
||||
"row": 1,
|
||||
"column": 1,
|
||||
}
|
||||
)
|
||||
self.env["kpi.dashboard.item"].create(
|
||||
{
|
||||
"dashboard_id": self.dashboard.id,
|
||||
"name": self.kpi_02.name,
|
||||
"kpi_id": self.kpi_02.id,
|
||||
"row": 1,
|
||||
"column": 2,
|
||||
}
|
||||
)
|
||||
self.env["kpi.dashboard.item"].create(
|
||||
{"dashboard_id": self.dashboard.id, "name": "TITLE", "row": 2, "column": 1,}
|
||||
)
|
||||
|
||||
def test_constrains_01(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_01.dashboard_item_ids.write({'size_x': 2})
|
||||
self.kpi_01.dashboard_item_ids.write({"size_x": 2})
|
||||
|
||||
def test_constrains_02(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_02.dashboard_item_ids.write({'size_x': 4})
|
||||
self.kpi_02.dashboard_item_ids.write({"size_x": 4})
|
||||
|
||||
def test_constrains_03(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.kpi_01.dashboard_item_ids.write({'size_y': 11})
|
||||
self.kpi_01.dashboard_item_ids.write({"size_y": 11})
|
||||
|
||||
def test_menu(self):
|
||||
self.assertFalse(self.dashboard.menu_id)
|
||||
wzd = self.env['kpi.dashboard.menu'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'menu_id': self.env['ir.ui.menu'].search([], limit=1).id,
|
||||
})
|
||||
wzd = self.env["kpi.dashboard.menu"].create(
|
||||
{
|
||||
"dashboard_id": self.dashboard.id,
|
||||
"menu_id": self.env["ir.ui.menu"].search([], limit=1).id,
|
||||
}
|
||||
)
|
||||
wzd.generate_menu()
|
||||
self.assertTrue(self.dashboard.menu_id)
|
||||
self.assertFalse(self.dashboard.menu_id.groups_id)
|
||||
self.dashboard.write({
|
||||
'group_ids': [
|
||||
(6, 0, self.env['res.groups'].search([], limit=1).ids)]
|
||||
})
|
||||
self.dashboard.write(
|
||||
{"group_ids": [(6, 0, self.env["res.groups"].search([], limit=1).ids)]}
|
||||
)
|
||||
self.assertTrue(self.dashboard.menu_id.groups_id)
|
||||
|
||||
def test_onchange(self):
|
||||
with Form(self.env['kpi.dashboard']) as dashboard:
|
||||
dashboard.name = 'New Dashboard'
|
||||
with Form(self.env["kpi.dashboard"]) as dashboard:
|
||||
dashboard.name = "New Dashboard"
|
||||
with dashboard.item_ids.new() as item:
|
||||
item.kpi_id = self.kpi_01
|
||||
self.assertTrue(item.name)
|
||||
|
@ -88,40 +95,38 @@ class TestKpiDashboard(TransactionCase):
|
|||
data = self.dashboard.read_dashboard()
|
||||
title_found = False
|
||||
actions = 0
|
||||
for item in data['item_ids']:
|
||||
if not item.get('kpi_id'):
|
||||
for item in data["item_ids"]:
|
||||
if not item.get("kpi_id"):
|
||||
title_found = True
|
||||
if item.get('actions', False):
|
||||
actions += len(item['actions'])
|
||||
if item.get("actions", False):
|
||||
actions += len(item["actions"])
|
||||
self.assertTrue(title_found)
|
||||
self.assertEqual(0, actions)
|
||||
act01 = self.env['ir.actions.act_window'].search(
|
||||
[], limit=1)
|
||||
self.env['kpi.kpi.action'].create({
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'action': '%s,%s' % (act01._name, act01.id)
|
||||
})
|
||||
act02 = self.env['ir.actions.act_url'].search(
|
||||
[], limit=1)
|
||||
self.env['kpi.kpi.action'].create({
|
||||
'kpi_id': self.kpi_01.id,
|
||||
'action': '%s,%s' % (act02._name, act02.id)
|
||||
})
|
||||
act01 = self.env["ir.actions.act_window"].search([], limit=1)
|
||||
self.env["kpi.kpi.action"].create(
|
||||
{"kpi_id": self.kpi_01.id, "action": "{},{}".format(act01._name, act01.id)}
|
||||
)
|
||||
act02 = self.env["ir.actions.act_url"].search([], limit=1)
|
||||
self.env["kpi.kpi.action"].create(
|
||||
{"kpi_id": self.kpi_01.id, "action": "{},{}".format(act02._name, act02.id)}
|
||||
)
|
||||
data = self.dashboard.read_dashboard()
|
||||
title_found = False
|
||||
actions = 0
|
||||
for item in data['item_ids']:
|
||||
if not item.get('kpi_id'):
|
||||
for item in data["item_ids"]:
|
||||
if not item.get("kpi_id"):
|
||||
title_found = True
|
||||
if item.get('actions', False):
|
||||
actions += len(item['actions'])
|
||||
if item.get("actions", False):
|
||||
actions += len(item["actions"])
|
||||
self.assertTrue(title_found)
|
||||
self.assertEqual(2, actions)
|
||||
self.assertFalse(data.get("action_id", False))
|
||||
wzd = self.env['kpi.dashboard.menu'].create({
|
||||
'dashboard_id': self.dashboard.id,
|
||||
'menu_id': self.env['ir.ui.menu'].search([], limit=1).id,
|
||||
})
|
||||
wzd = self.env["kpi.dashboard.menu"].create(
|
||||
{
|
||||
"dashboard_id": self.dashboard.id,
|
||||
"menu_id": self.env["ir.ui.menu"].search([], limit=1).id,
|
||||
}
|
||||
)
|
||||
wzd.generate_menu()
|
||||
data = self.dashboard.read_dashboard()
|
||||
self.assertTrue(data.get("action_id", False))
|
||||
|
@ -129,8 +134,8 @@ class TestKpiDashboard(TransactionCase):
|
|||
def test_compute(self):
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
with patch(
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi."
|
||||
"KpiKpi.test_demo_number", create=True
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi." "KpiKpi.test_demo_number",
|
||||
create=True,
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.compute()
|
||||
|
@ -138,10 +143,9 @@ class TestKpiDashboard(TransactionCase):
|
|||
|
||||
def test_compute_model(self):
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
self.kpi_01.model_id = self.env.ref('base.model_res_partner')
|
||||
self.kpi_01.model_id = self.env.ref("base.model_res_partner")
|
||||
with patch(
|
||||
"odoo.addons.base.models.res_partner.Partner.test_demo_number",
|
||||
create=True
|
||||
"odoo.addons.base.models.res_partner.Partner.test_demo_number", create=True
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.compute()
|
||||
|
@ -153,8 +157,8 @@ class TestKpiDashboard(TransactionCase):
|
|||
self.assertTrue(self.kpi_01.cron_id)
|
||||
self.assertFalse(self.kpi_01.value_last_update)
|
||||
with patch(
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi."
|
||||
"KpiKpi.test_demo_number", create=True
|
||||
"odoo.addons.kpi_dashboard.models.kpi_kpi." "KpiKpi.test_demo_number",
|
||||
create=True,
|
||||
) as f:
|
||||
f.return_value = {"value": 0}
|
||||
self.kpi_01.cron_id.method_direct_trigger()
|
||||
|
|
|
@ -1,170 +1,179 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Creu Blanca
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_form_view">
|
||||
<field name="name">kpi.dashboard.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header/>
|
||||
<header />
|
||||
<sheet>
|
||||
<div name="button_box" class="oe_button_box">
|
||||
<button name="%(kpi_dashboard.kpi_dashboard_menu_act_window)d"
|
||||
type="action"
|
||||
string="Generate menu"
|
||||
icon="fa-folder-open-o"
|
||||
context="{'default_dashboard_id': active_id}"
|
||||
attrs="{'invisible': [('menu_id', '!=', False)]}"
|
||||
/>
|
||||
<button
|
||||
name="%(kpi_dashboard.kpi_dashboard_menu_act_window)d"
|
||||
type="action"
|
||||
string="Generate menu"
|
||||
icon="fa-folder-open-o"
|
||||
context="{'default_dashboard_id': active_id}"
|
||||
attrs="{'invisible': [('menu_id', '!=', False)]}"
|
||||
/>
|
||||
</div>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="menu_id" attrs="{'invisible': [('menu_id', '=', False)]}"/>
|
||||
<field name="name" />
|
||||
<field
|
||||
name="menu_id"
|
||||
attrs="{'invisible': [('menu_id', '=', False)]}"
|
||||
/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page name="item" string="KPIs">
|
||||
<field name="item_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="kpi_id"/>
|
||||
<field name="column"/>
|
||||
<field name="row"/>
|
||||
<field name="size_x"/>
|
||||
<field name="size_y"/>
|
||||
<field name="color" widget="color"/>
|
||||
<field name="font_color" widget="color"/>
|
||||
<button name="technical_config" string=""
|
||||
type="object" icon="fa-edit"
|
||||
groups="base.group_no_one"/>
|
||||
<field name="name" />
|
||||
<field name="kpi_id" />
|
||||
<field name="column" />
|
||||
<field name="row" />
|
||||
<field name="size_x" />
|
||||
<field name="size_y" />
|
||||
<field name="color" widget="color" />
|
||||
<field name="font_color" widget="color" />
|
||||
<button
|
||||
name="technical_config"
|
||||
string=""
|
||||
type="object"
|
||||
icon="fa-edit"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page name="widget" string="Widget configuration">
|
||||
<group>
|
||||
<group name="margin">
|
||||
<field name="margin_x"/>
|
||||
<field name="margin_y"/>
|
||||
<field name="margin_x" />
|
||||
<field name="margin_y" />
|
||||
</group>
|
||||
<group name="dimension">
|
||||
<field name="widget_dimension_x"/>
|
||||
<field name="widget_dimension_y"/>
|
||||
<field name="number_of_columns"/>
|
||||
<field name="width"/>
|
||||
<field name="widget_dimension_x" />
|
||||
<field name="widget_dimension_y" />
|
||||
<field name="number_of_columns" />
|
||||
<field name="width" />
|
||||
</group>
|
||||
<group name="color">
|
||||
<field name="background_color" widget="color"/>
|
||||
<field name="background_color" widget="color" />
|
||||
</group>
|
||||
<group name="config">
|
||||
<field name="compute_on_fly_refresh"/>
|
||||
<field name="compute_on_fly_refresh" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page name="group" string="Groups">
|
||||
<field name="group_ids"/>
|
||||
<field name="group_ids" />
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_search_view">
|
||||
<field name="name">kpi.dashboard.search (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_tree_view">
|
||||
<field name="name">kpi.dashboard.tree (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_dashboard_view">
|
||||
<field name="name">kpi.dashboard.dashboard (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard</field>
|
||||
<field name="arch" type="xml">
|
||||
<dashboard/>
|
||||
<dashboard />
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="kpi_dashboard_act_window">
|
||||
<field name="name">Kpi Dashboard</field> <!-- TODO -->
|
||||
<field name="name">Kpi Dashboard</field>
|
||||
<!-- TODO -->
|
||||
<field name="res_model">kpi.dashboard</field>
|
||||
<field name="view_mode">tree,form,dashboard</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="kpi_dashboard_menu">
|
||||
<field name="name">Configure Dashboard</field>
|
||||
<field name="parent_id" ref="menu_configuration_kpi_dashboards"/> <!-- TODO -->
|
||||
<field name="action" ref="kpi_dashboard_act_window"/>
|
||||
<field name="sequence" eval="16"/> <!-- TODO -->
|
||||
<field name="parent_id" ref="menu_configuration_kpi_dashboards" />
|
||||
<!-- TODO -->
|
||||
<field name="action" ref="kpi_dashboard_act_window" />
|
||||
<field name="sequence" eval="16" />
|
||||
<!-- TODO -->
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_item_form_view">
|
||||
<field name="name">kpi.dashboard.item.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard.item</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header/>
|
||||
<header />
|
||||
<sheet>
|
||||
<div name="button_box" class="oe_button_box"/>
|
||||
<div name="button_box" class="oe_button_box" />
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="dashboard_id"/>
|
||||
<field name="kpi_id"/>
|
||||
<field name="column"/>
|
||||
<field name="row"/>
|
||||
<field name="size_x"/>
|
||||
<field name="size_y"/>
|
||||
<field name="color" widget="color"/>
|
||||
<field name="font_color" widget="color"/>
|
||||
<field name="name" />
|
||||
<field name="dashboard_id" />
|
||||
<field name="kpi_id" />
|
||||
<field name="column" />
|
||||
<field name="row" />
|
||||
<field name="size_x" />
|
||||
<field name="size_y" />
|
||||
<field name="color" widget="color" />
|
||||
<field name="font_color" widget="color" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_item_config_form_view">
|
||||
<field name="name">kpi.dashboard.item.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard.item</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header/>
|
||||
<header />
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="modify_context"/>
|
||||
<field name="modify_context_expression"
|
||||
attrs="{'invisible': [('modify_context', '=', False)]}"
|
||||
widget="ace" options="{'mode': 'python'}"/>
|
||||
<field name="modify_color"/>
|
||||
<field name="modify_color_expression"
|
||||
attrs="{'invisible': [('modify_color', '=', False)]}"
|
||||
widget="ace" options="{'mode': 'python'}"/>
|
||||
<field name="modify_context" />
|
||||
<field
|
||||
name="modify_context_expression"
|
||||
attrs="{'invisible': [('modify_context', '=', False)]}"
|
||||
widget="ace"
|
||||
options="{'mode': 'python'}"
|
||||
/>
|
||||
<field name="modify_color" />
|
||||
<field
|
||||
name="modify_color_expression"
|
||||
attrs="{'invisible': [('modify_color', '=', False)]}"
|
||||
widget="ace"
|
||||
options="{'mode': 'python'}"
|
||||
/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="write"
|
||||
string="Save" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button special="cancel" string="Cancel"
|
||||
class="oe_link"/>
|
||||
<button
|
||||
name="write"
|
||||
string="Save"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
<button special="cancel" string="Cancel" class="oe_link" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Creu Blanca
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record model="ir.actions.act_window" id="kpi_kpi_history_act_window">
|
||||
<field name="name">Kpi History</field>
|
||||
<field name="res_model">kpi.kpi.history</field>
|
||||
|
@ -11,149 +9,199 @@
|
|||
<field name="domain">[('kpi_id', '=', active_id)]</field>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_history_widget_form_view">
|
||||
<field name="name">kpi.kpi.history.raw.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="widget" invisible="1"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="value"
|
||||
widget="kpi"
|
||||
options="{'date': 'create_date', 'widget': 'widget', 'name': 'name'}"/>
|
||||
<field name="create_date" invisible="1" />
|
||||
<field name="widget" invisible="1" />
|
||||
<field name="name" invisible="1" />
|
||||
<field
|
||||
name="value"
|
||||
widget="kpi"
|
||||
options="{'date': 'create_date', 'widget': 'widget', 'name': 'name'}"
|
||||
/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_history_raw_form_view">
|
||||
<field name="name">kpi.kpi.history.raw.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="raw_value"/>
|
||||
<field name="raw_value" />
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_history_tree_view">
|
||||
<field name="name">kpi.kpi.history.tree (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree create="0" delete="0">
|
||||
<field name="create_date"/>
|
||||
<button name="show_form" string="Show value" type="object"
|
||||
context="{'form_id': %(kpi_dashboard.kpi_kpi_history_widget_form_view)d}"
|
||||
<field name="create_date" />
|
||||
<button
|
||||
name="show_form"
|
||||
string="Show value"
|
||||
type="object"
|
||||
context="{'form_id': %(kpi_dashboard.kpi_kpi_history_widget_form_view)d}"
|
||||
/>
|
||||
<button name="show_form" string="Raw value" type="object"
|
||||
context="{'form_id': %(kpi_dashboard.kpi_kpi_history_raw_form_view)d}"
|
||||
<button
|
||||
name="show_form"
|
||||
string="Raw value"
|
||||
type="object"
|
||||
context="{'form_id': %(kpi_dashboard.kpi_kpi_history_raw_form_view)d}"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_form_view">
|
||||
<field name="name">kpi.kpi.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button name="generate_cron" string="Generate cron" type="object"
|
||||
attrs="{'invisible': ['|', ('cron_id', '!=',False), ('compute_on_fly', '=', True)]}"/>
|
||||
<button name="compute" string="Compute now" type="object" attrs="{'invisible': [('compute_on_fly', '=', True)]}"/>
|
||||
<button
|
||||
name="generate_cron"
|
||||
string="Generate cron"
|
||||
type="object"
|
||||
attrs="{'invisible': ['|', ('cron_id', '!=',False), ('compute_on_fly', '=', True)]}"
|
||||
/>
|
||||
<button
|
||||
name="compute"
|
||||
string="Compute now"
|
||||
type="object"
|
||||
attrs="{'invisible': [('compute_on_fly', '=', True)]}"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(kpi_dashboard.kpi_kpi_history_act_window)d"
|
||||
string="Show history"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('store_history', '=', False), ('compute_on_fly', '=', True)]}"
|
||||
icon="fa-history"/>
|
||||
<button string="Show value" type="object" name="show_value"
|
||||
icon="fa-paint-brush"
|
||||
<button
|
||||
name="%(kpi_dashboard.kpi_kpi_history_act_window)d"
|
||||
string="Show history"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('store_history', '=', False), ('compute_on_fly', '=', True)]}"
|
||||
icon="fa-history"
|
||||
/>
|
||||
<button
|
||||
string="Show value"
|
||||
type="object"
|
||||
name="show_value"
|
||||
icon="fa-paint-brush"
|
||||
/>
|
||||
</div>
|
||||
<h2>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</h2>
|
||||
<group>
|
||||
<group>
|
||||
<field name="computation_method"/>
|
||||
<field name="widget"/>
|
||||
<field name="store_history" attrs="{'invisible': [('compute_on_fly', '=', True)]}"/>
|
||||
<field name="store_history_interval" attrs="{'invisible': [('store_history', '=', False)]}"/>
|
||||
<field name="store_history_interval_number" attrs="{'invisible': [('store_history', '=', False)]}"/>
|
||||
<field name="compute_on_fly" attrs="{'invisible': [('store_history', '=', True)]}"/>
|
||||
<field name="model_id" attrs="{'invisible': [('computation_method', '!=', 'function')]}"/>
|
||||
<field name="function" attrs="{'required': [('computation_method', '=', 'function')], 'invisible': [('computation_method', '!=', 'function')]}"/>
|
||||
<field name="args" attrs="{'invisible': [('computation_method', '!=', 'function')]}"/>
|
||||
<field name="kwargs" attrs="{'invisible': [('computation_method', '!=', 'function')]}"/>
|
||||
<field name="computation_method" />
|
||||
<field name="widget" />
|
||||
<field
|
||||
name="store_history"
|
||||
attrs="{'invisible': [('compute_on_fly', '=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="store_history_interval"
|
||||
attrs="{'invisible': [('store_history', '=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="store_history_interval_number"
|
||||
attrs="{'invisible': [('store_history', '=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="compute_on_fly"
|
||||
attrs="{'invisible': [('store_history', '=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="model_id"
|
||||
attrs="{'invisible': [('computation_method', '!=', 'function')]}"
|
||||
/>
|
||||
<field
|
||||
name="function"
|
||||
attrs="{'required': [('computation_method', '=', 'function')], 'invisible': [('computation_method', '!=', 'function')]}"
|
||||
/>
|
||||
<field
|
||||
name="args"
|
||||
attrs="{'invisible': [('computation_method', '!=', 'function')]}"
|
||||
/>
|
||||
<field
|
||||
name="kwargs"
|
||||
attrs="{'invisible': [('computation_method', '!=', 'function')]}"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="cron_id" attrs="{'invisible': [('cron_id', '=',False)]}" readonly="True"/>
|
||||
<field
|
||||
name="cron_id"
|
||||
attrs="{'invisible': [('cron_id', '=',False)]}"
|
||||
readonly="True"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="suffix"/>
|
||||
<field name="prefix"/>
|
||||
<field name="suffix" />
|
||||
<field name="prefix" />
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
<page name="action" string="Actions">
|
||||
<field name="action_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="action"/>
|
||||
<field name="context"/>
|
||||
<field name="action" />
|
||||
<field name="context" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page name="code" string="Code" attrs="{'invisible': [('computation_method', '!=', 'code')]}">
|
||||
<field name="code" widget="ace"
|
||||
options="{'mode': 'python'}"
|
||||
placeholder="Enter Python code here."/>
|
||||
<page
|
||||
name="code"
|
||||
string="Code"
|
||||
attrs="{'invisible': [('computation_method', '!=', 'code')]}"
|
||||
>
|
||||
<field
|
||||
name="code"
|
||||
widget="ace"
|
||||
options="{'mode': 'python'}"
|
||||
placeholder="Enter Python code here."
|
||||
/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_widget_form_view">
|
||||
<field name="name">kpi.kpi.raw.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="computed_date" invisible="1"/>
|
||||
<field name="widget" invisible="1"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="computed_value"
|
||||
widget="kpi"
|
||||
options="{'date': 'computed_date', 'widget': 'widget', 'name': 'name'}"/>
|
||||
<field name="computed_date" invisible="1" />
|
||||
<field name="widget" invisible="1" />
|
||||
<field name="name" invisible="1" />
|
||||
<field
|
||||
name="computed_value"
|
||||
widget="kpi"
|
||||
options="{'date': 'computed_date', 'widget': 'widget', 'name': 'name'}"
|
||||
/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_search_view">
|
||||
<field name="name">kpi.kpi.search (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_kpi_tree_view">
|
||||
<field name="name">kpi.kpi.tree (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.kpi</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="name" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="kpi_kpi_act_window">
|
||||
<field name="name">Kpi</field>
|
||||
<field name="res_model">kpi.kpi</field>
|
||||
|
@ -161,12 +209,10 @@
|
|||
<field name="domain">[]</field>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="kpi_kpi_menu">
|
||||
<field name="name">Configure Kpi</field>
|
||||
<field name="parent_id" ref="menu_configuration_kpi_dashboards"/>
|
||||
<field name="action" ref="kpi_kpi_act_window"/>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="parent_id" ref="menu_configuration_kpi_dashboards" />
|
||||
<field name="action" ref="kpi_kpi_act_window" />
|
||||
<field name="sequence" eval="20" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Creu Blanca
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo>
|
||||
<!-- CONFIGURATION -->
|
||||
<menuitem id="menu_configuration_kpi_dashboards"
|
||||
name="KPI Dashboards"
|
||||
parent="base.menu_reporting_config"
|
||||
groups="kpi_dashboard.group_kpi_dashboard_manager"
|
||||
sequence="10"/>
|
||||
<menuitem
|
||||
id="menu_configuration_kpi_dashboards"
|
||||
name="KPI Dashboards"
|
||||
parent="base.menu_reporting_config"
|
||||
groups="kpi_dashboard.group_kpi_dashboard_manager"
|
||||
sequence="10"
|
||||
/>
|
||||
</odoo>
|
||||
|
|
|
@ -1,31 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Creu Blanca
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="kpi_dashboard_menu_form_view">
|
||||
<field name="name">kpi.dashboard.menu.form (in kpi_dashboard)</field>
|
||||
<field name="model">kpi.dashboard.menu</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate Menu">
|
||||
<group>
|
||||
<field name="dashboard_id" invisible="1"/>
|
||||
<field name="menu_id"/>
|
||||
<field name="dashboard_id" invisible="1" />
|
||||
<field name="menu_id" />
|
||||
</group>
|
||||
<footer>
|
||||
<button name="generate_menu"
|
||||
string="Generate"
|
||||
class="btn-primary"
|
||||
type="object"/>
|
||||
<button string="Cancel"
|
||||
class="btn-default"
|
||||
special="cancel"/>
|
||||
<button
|
||||
name="generate_menu"
|
||||
string="Generate"
|
||||
class="btn-primary"
|
||||
type="object"
|
||||
/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="kpi_dashboard_menu_act_window">
|
||||
<field name="name">Kpi Dashboard Menu</field>
|
||||
<field name="res_model">kpi.dashboard.menu</field>
|
||||
|
@ -33,6 +30,4 @@
|
|||
<field name="context">{}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
|
|
Loading…
Reference in New Issue