diff --git a/kpi/models/kpi.py b/kpi/models/kpi.py index 7fb5e33f15..4050846510 100644 --- a/kpi/models/kpi.py +++ b/kpi/models/kpi.py @@ -144,6 +144,15 @@ def _compute_display_last_kpi_value(self): obj.color = "#FFFFFF" obj.last_execution = False + def _get_eval_context(self): + """Return the evaluation context used for Python KPI expressions. + + This method is meant to be extended by other modules to inject + additional safe globals into KPI formulas. + """ + self.ensure_one() + return {"self": self, "datetime": fields.datetime} + def _get_kpi_value(self): self.ensure_one() kpi_value = 0 @@ -163,9 +172,7 @@ def _get_kpi_value(self): if is_one_value(res): kpi_value = res[0]["value"] elif self.kpi_type == "python": - kpi_value = safe_eval( - self.kpi_code, {"self": self, "datetime": fields.datetime} - ) + kpi_value = safe_eval(self.kpi_code, self._get_eval_context()) if isinstance(kpi_value, dict): res = kpi_value else: diff --git a/kpi/tests/test_kpi.py b/kpi/tests/test_kpi.py index 4953efe3b2..a10ff199f6 100644 --- a/kpi/tests/test_kpi.py +++ b/kpi/tests/test_kpi.py @@ -127,3 +127,29 @@ def test_kpi_python(self): self.assertEqual(len(kpi_history), 1) self.assertEqual(kpi_history.color, "#00FF00") self.assertEqual(kpi_history.value, 1.0) + + def test_kpi_eval_context(self): + kpi_category = self.env["kpi.category"].create({"name": "Dynamic KPIs"}) + kpi_threshold = self.env["kpi.threshold"].create( + {"name": "KPI Threshold for dynamic KPIs"} + ) + kpi = self.env["kpi"].create( + { + "name": "KPI eval context", + "description": "KPI to test eval context", + "category_id": kpi_category.id, + "threshold_id": kpi_threshold.id, + "periodicity": 1, + "periodicity_uom": "day", + "kpi_type": "python", + "kpi_code": "self.name == 'KPI eval context' and 1.0 or 0.0", + } + ) + ctx = kpi._get_eval_context() + self.assertIn("self", ctx) + self.assertIn("datetime", ctx) + self.assertEqual(ctx["self"], kpi) + kpi.update_kpi_value() + kpi_history = self.env["kpi.history"].search([("kpi_id", "=", kpi.id)]) + self.assertEqual(len(kpi_history), 1) + self.assertEqual(kpi_history.value, 1.0)