[12.0][IMP] kpi_dashboard: Allow to use formula
parent
5b3aeed911
commit
3ca213d3e0
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
import ast
|
import ast
|
||||||
|
from odoo.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
|
|
||||||
class KpiKpi(models.Model):
|
class KpiKpi(models.Model):
|
||||||
|
@ -13,7 +14,7 @@ class KpiKpi(models.Model):
|
||||||
active = fields.Boolean(default=True)
|
active = fields.Boolean(default=True)
|
||||||
cron_id = fields.Many2one("ir.cron", readonly=True, copy=False)
|
cron_id = fields.Many2one("ir.cron", readonly=True, copy=False)
|
||||||
computation_method = fields.Selection(
|
computation_method = fields.Selection(
|
||||||
[("function", "Function")], required=True
|
[("function", "Function"), ("code", "Code")], required=True
|
||||||
)
|
)
|
||||||
value = fields.Serialized()
|
value = fields.Serialized()
|
||||||
dashboard_item_ids = fields.One2many("kpi.dashboard.item", inverse_name="kpi_id")
|
dashboard_item_ids = fields.One2many("kpi.dashboard.item", inverse_name="kpi_id")
|
||||||
|
@ -34,6 +35,7 @@ class KpiKpi(models.Model):
|
||||||
inverse_name='kpi_id',
|
inverse_name='kpi_id',
|
||||||
help="Actions that can be opened from the KPI"
|
help="Actions that can be opened from the KPI"
|
||||||
)
|
)
|
||||||
|
code = fields.Text("Code")
|
||||||
|
|
||||||
def _cron_vals(self):
|
def _cron_vals(self):
|
||||||
return {
|
return {
|
||||||
|
@ -84,6 +86,17 @@ class KpiKpi(models.Model):
|
||||||
vals["value_last_update"] = fields.Datetime.now()
|
vals["value_last_update"] = fields.Datetime.now()
|
||||||
return super().write(vals)
|
return super().write(vals)
|
||||||
|
|
||||||
|
def _get_code_input_dict(self):
|
||||||
|
return {
|
||||||
|
"self": self,
|
||||||
|
"model": self,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _compute_value_code(self):
|
||||||
|
results = self._get_code_input_dict()
|
||||||
|
safe_eval(self.code or "", results, mode="exec", nocopy=True)
|
||||||
|
return results.get("result", {})
|
||||||
|
|
||||||
|
|
||||||
class KpiKpiAction(models.Model):
|
class KpiKpiAction(models.Model):
|
||||||
_name = 'kpi.kpi.action'
|
_name = 'kpi.kpi.action'
|
||||||
|
|
|
@ -8,6 +8,9 @@ Configure KPIs
|
||||||
#. Meter: result must contain `value`, `min` and `max`
|
#. Meter: result must contain `value`, `min` and `max`
|
||||||
#. Graph: result must contain a list on `graphs` containing `values`, `title` and `key`
|
#. Graph: result must contain a list on `graphs` containing `values`, `title` and `key`
|
||||||
|
|
||||||
|
#. In order to compute the KPI you can use a predefined function from a model or
|
||||||
|
use the code to directly compute it.
|
||||||
|
|
||||||
|
|
||||||
Configure dashboards
|
Configure dashboards
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from . import test_formula
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Copyright 2020 Creu Blanca
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestFormula(TransactionCase):
|
||||||
|
def test_computation(self):
|
||||||
|
kpi = self.env["kpi.kpi"].create(
|
||||||
|
{
|
||||||
|
"name": "DEMO KPI",
|
||||||
|
"widget": "number",
|
||||||
|
"computation_method": "code",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertFalse(kpi.value)
|
||||||
|
kpi.compute()
|
||||||
|
self.assertEqual(kpi.value, {})
|
||||||
|
kpi.code = """
|
||||||
|
result = {}
|
||||||
|
result['value'] = len(model.search([('id', '=', %s)]))
|
||||||
|
result['previous'] = len(model.search([('id', '!=', %s)]))
|
||||||
|
""" % (
|
||||||
|
kpi.id,
|
||||||
|
kpi.id,
|
||||||
|
)
|
||||||
|
kpi.compute()
|
||||||
|
value = kpi.value
|
||||||
|
self.assertTrue(value.get("value"))
|
||||||
|
self.assertEqual(value.get("value"), 1)
|
||||||
|
self.assertEqual(value.get("previous"), kpi.search_count([]) - 1)
|
|
@ -44,7 +44,12 @@
|
||||||
<field name="action"/>
|
<field name="action"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</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>
|
||||||
</notebook>
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Reference in New Issue