Skip to content

Commit f889173

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 f889173

5 files changed

Lines changed: 115 additions & 7 deletions

File tree

mis_builder/models/kpimatrix.py

Lines changed: 24 additions & 4 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,7 +141,9 @@ 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)
@@ -159,6 +163,7 @@ def __init__(self, env, multi_company=False, account_model="account.account"):
159163
# { account_id: account_name }
160164
self._account_names = {}
161165
self._multi_company = multi_company
166+
self._query_companies = query_companies
162167

163168
def declare_kpi(self, kpi):
164169
"""Declare a new kpi (row) in the matrix.
@@ -467,9 +472,24 @@ def _load_account_names(self):
467472
self._account_names = {a.id: self._get_account_name(a) for a in accounts}
468473

469474
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}]"
475+
account_code = account.code
476+
result = f"{account_code} {account.name}"
477+
if not self._multi_company:
478+
return result
479+
# Multi company report
480+
account_companies = account.company_ids.filtered_domain(
481+
[("id", "in", self._query_companies.ids)]
482+
)
483+
# Maybe account belongs to a company that is not the current env company
484+
# and has no visible code, so choose the first one and get the code
485+
if not account_code:
486+
account_code = account.with_company(account_companies[:1]).code
487+
company_names = account_companies.mapped("name")
488+
if len(company_names) > COMPANY_NAMES_DISPLAY_LIMIT:
489+
company_names = company_names[:COMPANY_NAMES_DISPLAY_LIMIT] + [
490+
f"and {len(company_names) - COMPANY_NAMES_DISPLAY_LIMIT} more"
491+
]
492+
result = f"{account_code} {account.name} [{', '.join(company_names)}]"
473493
return result
474494

475495
def get_account_name(self, account_id):

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: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
# Create KpiMatrix instance with chosen companies
29+
return KpiMatrix(
30+
mock_env, multi_company=len(companies) > 1, query_companies=companies
31+
)
32+
33+
def _create_mock_account(self, code, name, company_ids):
34+
"""Helper to create mock account objects"""
35+
account = Mock()
36+
account.code = code
37+
account.name = name
38+
account.company_ids = company_ids or []
39+
return account
40+
41+
def _create_nbr_companies(self, number_of_companies):
42+
"""Helper to create company objects"""
43+
companies = self.env["res.company"]
44+
for name in [f"Company {i + 1}" for i in range(number_of_companies)]:
45+
companies |= self.env["res.company"].create({"name": name})
46+
return companies
47+
48+
def test_get_account_name_single_company(self):
49+
"""Test account name without company info in single company mode"""
50+
company = self._create_nbr_companies(1)
51+
single_company_matrix = self._create_mock_env_kpimatrix(company)
52+
account = self._create_mock_account("100", "Cash", company)
53+
result = single_company_matrix._get_account_name(account)
54+
self.assertEqual(
55+
result,
56+
"100 Cash",
57+
"Account name are not displayed correctly on single company",
58+
)
59+
60+
def test_get_account_name_with_company_ids_multiple(self):
61+
"""Test account name with company_ids field containing multiple
62+
companies (≤ 3)"""
63+
companies = self._create_nbr_companies(COMPANY_NAMES_DISPLAY_LIMIT)
64+
account = self._create_mock_account("300", "Receivables", companies)
65+
kpi_matrix = self._create_mock_env_kpimatrix(companies)
66+
result = kpi_matrix._get_account_name(account)
67+
self.assertEqual(
68+
result,
69+
"300 Receivables [Company 1, Company 2, Company 3]",
70+
"Company names on account name are not displayed correctly",
71+
)
72+
73+
def test_get_account_name_with_company_ids_many(self):
74+
"""Test account name with company_ids field containing > N companies"""
75+
companies = self._create_nbr_companies(COMPANY_NAMES_DISPLAY_LIMIT + 1)
76+
account = self._create_mock_account("400", "Payables", companies)
77+
kpi_matrix = self._create_mock_env_kpimatrix(companies)
78+
result = kpi_matrix._get_account_name(account)
79+
self.assertEqual(
80+
result,
81+
"400 Payables [Company 1, Company 2, Company 3, and 1 more]",
82+
"Company names on account name should be truncated",
83+
)

0 commit comments

Comments
 (0)