[12.0][IMP] kpi_dashboard: Compute the KPI on a separate cursor and rollback it
parent
bbe73a0063
commit
fbbc9a28ae
|
@ -21,4 +21,5 @@
|
||||||
"views/kpi_dashboard.xml",
|
"views/kpi_dashboard.xml",
|
||||||
],
|
],
|
||||||
"demo": ["demo/demo_dashboard.xml"],
|
"demo": ["demo/demo_dashboard.xml"],
|
||||||
|
"maintainers": ["etobella"],
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,6 @@ result = {"graphs": [
|
||||||
<function model="kpi.kpi" name="compute"
|
<function model="kpi.kpi" name="compute"
|
||||||
eval="[[ref('widget_graph')]]"/>
|
eval="[[ref('widget_graph')]]"/>
|
||||||
|
|
||||||
|
|
||||||
<record id="dashboard_widget_text" model="kpi.dashboard.item">
|
<record id="dashboard_widget_text" model="kpi.dashboard.item">
|
||||||
<field name="name">Dashboard title</field>
|
<field name="name">Dashboard title</field>
|
||||||
<field name="dashboard_id" ref="demo_dashboard"/>
|
<field name="dashboard_id" ref="demo_dashboard"/>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
# Copyright 2020 Creu Blanca
|
# Copyright 2020 Creu Blanca
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
import ast
|
import ast
|
||||||
from odoo.tools.safe_eval import safe_eval
|
from odoo.tools.safe_eval import safe_eval
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
class KpiKpi(models.Model):
|
class KpiKpi(models.Model):
|
||||||
|
@ -89,12 +91,28 @@ class KpiKpi(models.Model):
|
||||||
def _get_code_input_dict(self):
|
def _get_code_input_dict(self):
|
||||||
return {
|
return {
|
||||||
"self": self,
|
"self": self,
|
||||||
"model": self,
|
"model": self.browse(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _forbidden_code(self):
|
||||||
|
return ["commit", "rollback", "getattr", "execute"]
|
||||||
|
|
||||||
def _compute_value_code(self):
|
def _compute_value_code(self):
|
||||||
|
forbidden = self._forbidden_code()
|
||||||
|
search_terms = "(" + ("|".join(forbidden)) + ")"
|
||||||
|
if re.search(search_terms, (self.code or "").lower()):
|
||||||
|
message = ", ".join(forbidden[:-1]) or ""
|
||||||
|
if len(message) > 0:
|
||||||
|
message += _(" or ")
|
||||||
|
message += forbidden[-1]
|
||||||
|
raise ValidationError(_(
|
||||||
|
"The code cannot contain the following terms: %s."
|
||||||
|
) % message)
|
||||||
results = self._get_code_input_dict()
|
results = self._get_code_input_dict()
|
||||||
|
savepoint = "kpi_formula_%s" % self.id
|
||||||
|
self.env.cr.execute("savepoint %s" % savepoint)
|
||||||
safe_eval(self.code or "", results, mode="exec", nocopy=True)
|
safe_eval(self.code or "", results, mode="exec", nocopy=True)
|
||||||
|
self.env.cr.execute("rollback to %s" % savepoint)
|
||||||
return results.get("result", {})
|
return results.get("result", {})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,30 +2,58 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class TestFormula(TransactionCase):
|
class TestFormula(TransactionCase):
|
||||||
def test_computation(self):
|
def setUp(self):
|
||||||
kpi = self.env["kpi.kpi"].create(
|
super().setUp()
|
||||||
|
self.kpi = self.env["kpi.kpi"].create(
|
||||||
{
|
{
|
||||||
"name": "DEMO KPI",
|
"name": "DEMO KPI",
|
||||||
"widget": "number",
|
"widget": "number",
|
||||||
"computation_method": "code",
|
"computation_method": "code",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertFalse(kpi.value)
|
|
||||||
kpi.compute()
|
def test_forbidden_words_01(self):
|
||||||
self.assertEqual(kpi.value, {})
|
self.kpi.code = """
|
||||||
kpi.code = """
|
result = {"value": 0}
|
||||||
|
self.env.cr.commit()
|
||||||
|
"""
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
self.kpi.compute()
|
||||||
|
|
||||||
|
def test_forbidden_words_02(self):
|
||||||
|
self.kpi.code = """
|
||||||
|
result = {"value": 0}
|
||||||
|
self.env.cr.rollback()
|
||||||
|
"""
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
self.kpi.compute()
|
||||||
|
|
||||||
|
def test_forbidden_words_03(self):
|
||||||
|
self.kpi.code = """
|
||||||
|
result = {"value": 0}
|
||||||
|
self.env.cr.execute("CoMMiT")
|
||||||
|
"""
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
self.kpi.compute()
|
||||||
|
|
||||||
|
def test_computation(self):
|
||||||
|
self.assertFalse(self.kpi.value)
|
||||||
|
self.kpi.compute()
|
||||||
|
self.assertEqual(self.kpi.value, {})
|
||||||
|
self.kpi.code = """
|
||||||
result = {}
|
result = {}
|
||||||
result['value'] = len(model.search([('id', '=', %s)]))
|
result['value'] = len(model.search([('id', '=', %s)]))
|
||||||
result['previous'] = len(model.search([('id', '!=', %s)]))
|
result['previous'] = len(model.search([('id', '!=', %s)]))
|
||||||
""" % (
|
""" % (
|
||||||
kpi.id,
|
self.kpi.id,
|
||||||
kpi.id,
|
self.kpi.id,
|
||||||
)
|
)
|
||||||
kpi.compute()
|
self.kpi.compute()
|
||||||
value = kpi.value
|
value = self.kpi.value
|
||||||
self.assertTrue(value.get("value"))
|
self.assertTrue(value.get("value"))
|
||||||
self.assertEqual(value.get("value"), 1)
|
self.assertEqual(value.get("value"), 1)
|
||||||
self.assertEqual(value.get("previous"), kpi.search_count([]) - 1)
|
self.assertEqual(value.get("previous"), self.kpi.search_count([]) - 1)
|
||||||
|
|
Loading…
Reference in New Issue