Skip to content

Commit 4100527

Browse files
ShideStefanRijnhartgeraldo29
committed
[FIX] mis_builder: account.account multi company support for Odoo 18
Co-authored-by: Stefan Rijnhart <1033124+StefanRijnhart@users.noreply.github.com> Co-authored-by: Geraldo Lopez <5943428+geraldo29@users.noreply.github.com>
1 parent 7f466d4 commit 4100527

7 files changed

Lines changed: 301 additions & 650 deletions

File tree

mis_builder/i18n/es.po

Lines changed: 176 additions & 642 deletions
Large diffs are not rendered by default.

mis_builder/i18n/mis_builder.pot

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ msgid ""
66
msgstr ""
77
"Project-Id-Version: Odoo Server 18.0\n"
88
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2025-11-26 10:41+0000\n"
10+
"PO-Revision-Date: 2025-11-26 10:41+0000\n"
911
"Last-Translator: \n"
1012
"Language-Team: \n"
1113
"MIME-Version: 1.0\n"
@@ -1735,6 +1737,12 @@ msgstr ""
17351737
msgid "`fld` must have a field name in exression %s"
17361738
msgstr ""
17371739

1740+
#. module: mis_builder
1741+
#. odoo-python
1742+
#: code:addons/mis_builder/models/kpimatrix.py:0
1743+
msgid "and %s more"
1744+
msgstr ""
1745+
17381746
#. module: mis_builder
17391747
#. odoo-python
17401748
#: code:addons/mis_builder/models/mis_report_instance.py:0

mis_builder/models/kpimatrix.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from .mis_safe_eval import DataError, mis_safe_eval
1212
from .simple_array import SimpleArray
1313

14+
COMPANY_NAMES_DISPLAY_LIMIT = 3
15+
1416
_logger = logging.getLogger(__name__)
1517

1618

@@ -139,12 +141,15 @@ def __init__(
139141

140142

141143
class KpiMatrix:
142-
def __init__(self, env, multi_company=False, account_model="account.account"):
144+
def __init__(
145+
self, env, multi_company, query_companies, account_model="account.account"
146+
):
143147
# cache language id for faster rendering
144148
lang_model = env["res.lang"]
145149
self.lang = lang_model._lang_get(env.user.lang)
146150
self._style_model = env["mis.report.style"]
147151
self._account_model = env[account_model]
152+
self._ = env._
148153
# data structures
149154
# { kpi: KpiMatrixRow }
150155
self._kpi_rows = OrderedDict()
@@ -159,6 +164,7 @@ def __init__(self, env, multi_company=False, account_model="account.account"):
159164
# { account_id: account_name }
160165
self._account_names = {}
161166
self._multi_company = multi_company
167+
self._query_companies = query_companies
162168

163169
def declare_kpi(self, kpi):
164170
"""Declare a new kpi (row) in the matrix.
@@ -467,10 +473,24 @@ def _load_account_names(self):
467473
self._account_names = {a.id: self._get_account_name(a) for a in accounts}
468474

469475
def _get_account_name(self, account):
470-
result = f"{account.code} {account.name}"
471-
if self._multi_company:
472-
result = f"{result} [{account.company_id.name}]"
473-
return result
476+
account_code = account.code
477+
result = f"{account_code} {account.name}"
478+
if not self._multi_company:
479+
return result
480+
# Multi company report
481+
account_companies = account.company_ids.filtered_domain(
482+
[("id", "in", self._query_companies.ids)]
483+
)
484+
# Maybe account belongs to a company that is not the current env company
485+
# and has no visible code, so choose the first one and get the code
486+
if not account_code:
487+
account_code = account.with_company(account_companies[:1]).code
488+
company_names = account_companies.mapped("name")
489+
if len(company_names) > COMPANY_NAMES_DISPLAY_LIMIT:
490+
company_names = company_names[:COMPANY_NAMES_DISPLAY_LIMIT] + [
491+
self._("and %s more", len(company_names) - COMPANY_NAMES_DISPLAY_LIMIT)
492+
]
493+
return f"{account_code} {account.name} [{', '.join(company_names)}]"
474494

475495
def get_account_name(self, account_id):
476496
if account_id not in self._account_names:

mis_builder/models/mis_report.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,9 +532,11 @@ def copy(self, default=None):
532532

533533
# TODO: kpi name cannot be start with query name
534534

535-
def prepare_kpi_matrix(self, multi_company=False):
535+
def prepare_kpi_matrix(self, multi_company=False, query_companies=None):
536536
self.ensure_one()
537-
kpi_matrix = KpiMatrix(self.env, multi_company, self.account_model)
537+
kpi_matrix = KpiMatrix(
538+
self.env, multi_company, query_companies, self.account_model
539+
)
538540
for kpi in self.kpi_ids:
539541
kpi_matrix.declare_kpi(kpi)
540542
return kpi_matrix

mis_builder/models/mis_report_instance.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,9 @@ def _compute_matrix(self):
878878
self.ensure_one()
879879
aep = self.report_id._prepare_aep(self.query_company_ids, self.currency_id)
880880
multi_company = self.multi_company and len(self.query_company_ids) > 1
881-
kpi_matrix = self.report_id.prepare_kpi_matrix(multi_company)
881+
kpi_matrix = self.report_id.prepare_kpi_matrix(
882+
multi_company=multi_company, query_companies=self.query_company_ids
883+
)
882884
for period in self.period_ids:
883885
description = None
884886
if period.mode == MODE_NONE:

mis_builder/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from . import test_aggregate
88
from . import test_data_sources
99
from . import test_kpi_data
10+
from . import test_kpimatrix_account_names
1011
from . import test_mis_report_instance
1112
from . import test_mis_safe_eval
1213
from . import test_period_dates
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright 2025 Geraldo Lopez
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
3+
4+
from unittest.mock import MagicMock, Mock
5+
6+
from odoo.tests import common
7+
8+
from ..models.kpimatrix import COMPANY_NAMES_DISPLAY_LIMIT, KpiMatrix
9+
10+
11+
class TestKPIMatrixAccountNames(common.TransactionCase):
12+
"""Unit tests for KpiMatrix._get_account_name() method enhancements"""
13+
14+
def _create_mock_env_kpimatrix(self, companies):
15+
"""Helper to create KpiMatrix instances"""
16+
# Create a proper mock environment for KpiMatrix
17+
mock_env = MagicMock()
18+
# Mock the required models and services
19+
mock_lang_model = Mock()
20+
mock_lang_model._lang_get.return_value = Mock()
21+
mock_env.__getitem__.side_effect = lambda key: {
22+
"res.lang": mock_lang_model,
23+
"mis.report.style": Mock(),
24+
"account.account": Mock(),
25+
}.get(key, Mock())
26+
# Mock user with language
27+
mock_env.user.lang = "en_US"
28+
mock_env._ = self.env._
29+
# Create KpiMatrix instance with chosen companies
30+
return KpiMatrix(
31+
mock_env, multi_company=len(companies) > 1, query_companies=companies
32+
)
33+
34+
def _create_mock_account(self, code, name, company_ids):
35+
"""Helper to create mock account objects"""
36+
account = Mock()
37+
account.code = code
38+
account.name = name
39+
account.company_ids = company_ids or []
40+
return account
41+
42+
def _create_nbr_companies(self, number_of_companies):
43+
"""Helper to create company objects"""
44+
companies = self.env["res.company"]
45+
for name in [f"Company {i + 1}" for i in range(number_of_companies)]:
46+
companies |= self.env["res.company"].create({"name": name})
47+
return companies
48+
49+
def test_get_account_name_single_company(self):
50+
"""Test account name without company info in single company mode"""
51+
company = self._create_nbr_companies(1)
52+
single_company_matrix = self._create_mock_env_kpimatrix(company)
53+
account = self._create_mock_account("100", "Cash", company)
54+
result = single_company_matrix._get_account_name(account)
55+
self.assertEqual(
56+
result,
57+
"100 Cash",
58+
"Account name are not displayed correctly on single company",
59+
)
60+
61+
def test_get_account_name_with_company_ids_multiple(self):
62+
"""Test account name with company_ids field containing multiple
63+
companies (≤ 3)"""
64+
companies = self._create_nbr_companies(COMPANY_NAMES_DISPLAY_LIMIT)
65+
account = self._create_mock_account("300", "Receivables", companies)
66+
kpi_matrix = self._create_mock_env_kpimatrix(companies)
67+
result = kpi_matrix._get_account_name(account)
68+
self.assertEqual(
69+
result,
70+
"300 Receivables [Company 1, Company 2, Company 3]",
71+
"Company names on account name are not displayed correctly",
72+
)
73+
74+
def test_get_account_name_with_company_ids_many(self):
75+
"""Test account name with company_ids field containing > N companies"""
76+
companies = self._create_nbr_companies(COMPANY_NAMES_DISPLAY_LIMIT + 1)
77+
account = self._create_mock_account("400", "Payables", companies)
78+
kpi_matrix = self._create_mock_env_kpimatrix(companies)
79+
result = kpi_matrix._get_account_name(account)
80+
self.assertEqual(
81+
result,
82+
"400 Payables [Company 1, Company 2, Company 3, and 1 more]",
83+
"Company names on account name should be truncated",
84+
)

0 commit comments

Comments
 (0)