diff --git a/mis_builder_budget/models/mis_report_instance.py b/mis_builder_budget/models/mis_report_instance.py index 5a095031..da3207ec 100644 --- a/mis_builder_budget/models/mis_report_instance.py +++ b/mis_builder_budget/models/mis_report_instance.py @@ -1,7 +1,7 @@ # Copyright 2017 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models +from odoo import api, models from odoo.osv.expression import AND from odoo.addons.mis_builder.models.accounting_none import AccountingNone @@ -21,10 +21,23 @@ def __init__(self, date_from, date_to, kpi_data, additional_move_line_filter): ) self.kpi_data = kpi_data + @api.model + def _get_kpi_for_expressions(self, expressions): + kpi = None + for expression in expressions: + if not expression: + continue + if kpi is None: + kpi = expression.kpi_id + else: + assert ( + kpi == expression.kpi_id + ), "expressions must belong to the same kpi" + return kpi + def eval_expressions(self, expressions, locals_dict): - kpi_id = expressions.mapped("kpi_id") - assert len(kpi_id) == 1 - if kpi_id.budgetable: + kpi = self._get_kpi_for_expressions(expressions) + if kpi and kpi.budgetable: vals = [] drilldown_args = [] for expression in expressions: diff --git a/mis_builder_budget/readme/newsfragments/428.bugfix b/mis_builder_budget/readme/newsfragments/428.bugfix new file mode 100644 index 00000000..36ee4177 --- /dev/null +++ b/mis_builder_budget/readme/newsfragments/428.bugfix @@ -0,0 +1 @@ +Fix display of budgets in presence of sub KPIs. diff --git a/mis_builder_budget/tests/__init__.py b/mis_builder_budget/tests/__init__.py index 699440c2..08d8a279 100644 --- a/mis_builder_budget/tests/__init__.py +++ b/mis_builder_budget/tests/__init__.py @@ -1,2 +1,3 @@ +from . import test_expression_evaluator from . import test_mis_budget from . import test_mis_budget_by_account diff --git a/mis_builder_budget/tests/test_expression_evaluator.py b/mis_builder_budget/tests/test_expression_evaluator.py new file mode 100644 index 00000000..07a1f23c --- /dev/null +++ b/mis_builder_budget/tests/test_expression_evaluator.py @@ -0,0 +1,68 @@ +# Copyright 2023 ACSONE SA/NV () +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import SavepointCase + +from odoo.addons.mis_builder_budget.models.mis_report_instance import ( + MisBudgetAwareExpressionEvaluator, +) + + +class TestBudgetAwareExpressionEvaluator(SavepointCase): + def _make_evaluator(self, kpi_data=None): + return MisBudgetAwareExpressionEvaluator( + date_from="2017-01-01", + date_to="2017-01-16", + kpi_data=kpi_data or {}, + additional_move_line_filter=[], + ) + + def test_no_expression(self): + evaluator = self._make_evaluator() + evaluator.eval_expressions([None], locals_dict={}) + + def test_one_expression(self): + evaluator = self._make_evaluator() + kpi = self.env["mis.report.kpi"].new({"name": "thekpi"}) + expr = self.env["mis.report.kpi.expression"].new( + {"kpi_id": kpi.id, "name": "a"} + ) + vals, drilldown_args, name_error = evaluator.eval_expressions( + [expr], locals_dict={"a": 1} + ) + self.assertEqual(vals, [1]) + self.assertEqual(drilldown_args, [None]) + self.assertFalse(name_error) + + def test_two_expressions(self): + evaluator = self._make_evaluator() + kpi = self.env["mis.report.kpi"].new({"name": "thekpi"}) + expr1 = self.env["mis.report.kpi.expression"].new( + {"kpi_id": kpi.id, "name": "a"} + ) + expr2 = self.env["mis.report.kpi.expression"].new( + {"kpi_id": kpi.id, "name": "b"} + ) + vals, drilldown_args, _ = evaluator.eval_expressions( + [expr1, expr2], locals_dict={"a": 1, "b": 2} + ) + self.assertEqual(vals, [1, 2]) + self.assertEqual(drilldown_args, [None, None]) + + def test_two_expressions_with_budget(self): + kpi = self.env["mis.report.kpi"].new({"name": "thekpi", "budgetable": True}) + expr1 = self.env["mis.report.kpi.expression"].new( + {"kpi_id": kpi.id, "name": "a"} + ) + expr2 = self.env["mis.report.kpi.expression"].new( + {"kpi_id": kpi.id, "name": "b"} + ) + kpi_data = { + expr1: 10, + expr2: 20, + } + evaluator = self._make_evaluator(kpi_data) + vals, _, _ = evaluator.eval_expressions( + [expr1, expr2], locals_dict={"a": 1, "b": 2} + ) + self.assertEqual(vals, [10, 20]) # budget values instead of locals_dict values