diff --git a/mis_builder/README.rst b/mis_builder/README.rst index 48aacc608..f02f7e712 100644 --- a/mis_builder/README.rst +++ b/mis_builder/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - =========== MIS Builder =========== @@ -17,7 +13,7 @@ MIS Builder .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png :target: https://odoo-community.org/page/development-status :alt: Production/Stable -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmis--builder-lightgray.png?logo=github diff --git a/mis_builder/__manifest__.py b/mis_builder/__manifest__.py index 1cf14dc59..ece29c3e9 100644 --- a/mis_builder/__manifest__.py +++ b/mis_builder/__manifest__.py @@ -29,6 +29,9 @@ "report/mis_report_instance_qweb.xml", "report/mis_report_instance_xlsx.xml", ], + "demo": [ + "demo/mis_multicompany_demo.xml", + ], "assets": { "web.assets_backend": [ "mis_builder/static/src/components/mis_report_widget.esm.js", diff --git a/mis_builder/demo/mis_multicompany_demo.xml b/mis_builder/demo/mis_multicompany_demo.xml new file mode 100644 index 000000000..44170bd9f --- /dev/null +++ b/mis_builder/demo/mis_multicompany_demo.xml @@ -0,0 +1,147 @@ + + + + + + Other Company + + + + + + + + + + 600000 + Expenses + expense + + + + + + 611000 + Purchase of Equipments + expense + + + + + + 101401 + Bank + asset_current + + + + + + Miscellaneous + general + MISC + + + + + + + + 2026-03-10 + + + + + + + + 2026-02-20 + + + + + + Multi-Company Expenses + + + + + exp + Expenses + True + 10 + + + + + balp[600%] + + + + + equip + Equipment + True + 20 + + + + + balp[611%] + + + + + total + Total + 100 + AccountingTotal + + + + + exp + equip + + + + + Multi-Company Expenses Report + + True + True + + + + + + + Year to Date + relative + actuals + y + 0 + 1 + 10 + + + + + diff --git a/mis_builder/models/kpimatrix.py b/mis_builder/models/kpimatrix.py index 63c706d7e..77d760ff8 100644 --- a/mis_builder/models/kpimatrix.py +++ b/mis_builder/models/kpimatrix.py @@ -144,6 +144,7 @@ def __init__( env, companies=None, account_model="account.account", + companies_as_columns=False, ): # cache language id for faster rendering lang_model = env["res.lang"] @@ -151,6 +152,7 @@ def __init__( self._style_model = env["mis.report.style"] self._account_model = env[account_model] self._companies = companies + self._companies_as_columns = companies_as_columns # data structures # { kpi: KpiMatrixRow } self._kpi_rows = OrderedDict() @@ -489,7 +491,11 @@ def _get_account_name(self, account): # (this may return a name without code) account_name = account.display_name is_multi_company = self._companies and len(self._companies) > 1 - if is_multi_company and len(account_companies) == 1: + if ( + is_multi_company + and len(account_companies) == 1 + and not self._companies_as_columns + ): # In a multi-company report, if the account is bound to one # company, it makes sense to show the company name. If the account # is bound to multiple companies it does not make sense, because we @@ -498,6 +504,8 @@ def _get_account_name(self, account): # information. To be able to accurately display the company on # detail lines when the account is bound to multiple companies, # we'll need a generalized kpi details expansion. + # When companies are shown as separate columns, each column is + # already a single company, so the per-row suffix is redundant. account_name = f"{account_name} [{account_companies.display_name}]" return account_name diff --git a/mis_builder/models/mis_report.py b/mis_builder/models/mis_report.py index e5f0cad42..6c2ff49c6 100644 --- a/mis_builder/models/mis_report.py +++ b/mis_builder/models/mis_report.py @@ -539,9 +539,14 @@ def copy(self, default=None): # TODO: kpi name cannot be start with query name - def prepare_kpi_matrix(self, companies=None): + def prepare_kpi_matrix(self, companies=None, companies_as_columns=False): self.ensure_one() - kpi_matrix = KpiMatrix(self.env, companies, self.account_model) + kpi_matrix = KpiMatrix( + self.env, + companies, + self.account_model, + companies_as_columns=companies_as_columns, + ) for kpi in self.kpi_ids: kpi_matrix.declare_kpi(kpi) return kpi_matrix diff --git a/mis_builder/models/mis_report_instance.py b/mis_builder/models/mis_report_instance.py index 070ffe1a5..a85a465cf 100644 --- a/mis_builder/models/mis_report_instance.py +++ b/mis_builder/models/mis_report_instance.py @@ -230,6 +230,12 @@ def _compute_dates(self): default=1, ) subkpi_ids = fields.Many2many("mis.report.subkpi", string="Sub KPI Filter") + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + help="Filter this column to a single company. " + "Leave empty to include all allowed companies.", + ) source = fields.Selection( [ @@ -393,6 +399,8 @@ def _get_additional_move_line_filter(self): return [] # First get the report-level filter domain. domain = self.report_instance_id._get_filter_domain(self.source_aml_model_name) + if self.company_id: + domain.append(("company_id", "=", self.company_id.id)) if self.analytic_domain: # Then extend it with the column-level analytic domain. domain.extend(ast.literal_eval(self.analytic_domain)) @@ -411,9 +419,18 @@ def _get_additional_query_filter(self, query): self.ensure_one() domain = [] if company_field := query.sudo().company_field_id: - query_company_ids = self.report_instance_id.query_company_ids.ids - assert query_company_ids - domain = [(company_field.name, "in", query_company_ids)] + if self.company_id: + domain = [(company_field.name, "=", self.company_id.id)] + else: + query_company_ids = self.report_instance_id.query_company_ids.ids + if not query_company_ids: + raise UserError( + self.env._( + "No companies are configured for report '%s'.", + self.report_instance_id.name, + ) + ) + domain = [(company_field.name, "in", query_company_ids)] return domain @api.constrains("mode", "source") @@ -771,6 +788,49 @@ def get_views(self, views, options=None): result = super().get_views(views, options) return result + def action_generate_company_columns(self): + """Generate one period/column per company in company_ids + Total.""" + self.ensure_one() + if not self.multi_company or len(self.company_ids) < 2: + raise UserError( + self.env._( + "Please enable Multi Company and select at least 2 companies " + "before generating company columns." + ) + ) + self.period_ids.unlink() + seq = 10 + period_ids = [] + for company in self.company_ids: + period = self.env["mis.report.instance.period"].create( + { + "report_instance_id": self.id, + "name": company.name, + "mode": MODE_REL, + "source": SRC_ACTUALS, + "type": "y", + "offset": 0, + "duration": 1, + "sequence": seq, + "company_id": company.id, + } + ) + period_ids.append(period.id) + seq += 10 + self.env["mis.report.instance.period"].create( + { + "report_instance_id": self.id, + "name": self.env._("Total"), + "mode": MODE_NONE, + "source": SRC_SUMCOL, + "sequence": seq, + "source_sumcol_ids": [ + (0, 0, {"period_to_sum_id": pid, "sign": "+"}) for pid in period_ids + ], + } + ) + self.comparison_mode = True + def preview(self): self.ensure_one() view_id = self.env.ref("mis_builder.mis_report_instance_result_view_form") @@ -877,7 +937,15 @@ def _compute_matrix(self): """ self.ensure_one() aep = self.report_id._prepare_aep(self.query_company_ids, self.currency_id) - kpi_matrix = self.report_id.prepare_kpi_matrix(self.query_company_ids) + multi_company = self.multi_company and len(self.query_company_ids) > 1 + companies_as_columns = multi_company and all( + p.company_id + for p in self.period_ids + if p.source in (SRC_ACTUALS, SRC_ACTUALS_ALT) + ) + kpi_matrix = self.report_id.prepare_kpi_matrix( + self.query_company_ids, companies_as_columns + ) for period in self.period_ids: description = None if period.mode == MODE_NONE: @@ -1023,3 +1091,82 @@ def _compute_user_can_edit_annotation(self): self.user_can_edit_annotation = self.env.user.has_group( "mis_builder.group_edit_annotation" ) + + @api.model + def _post_demo_moves(self): + """Post demo journal entries for the multi-company demo report.""" + # Post Other Company moves created via XML demo data + for xmlid in [ + "mis_builder.move_other_consulting", + "mis_builder.move_other_furniture", + ]: + move = self.env.ref(xmlid, raise_if_not_found=False) + if move and move.state == "draft": + try: + move.with_company(move.company_id).action_post() + except Exception: + _logger.warning("Could not post demo move %s", xmlid) + # Create and post YourCompany demo moves so all columns show values + main_company = self.env.ref("base.main_company") + expense_account = self.env["account.account"].search( + [ + ("company_ids", "in", [main_company.id]), + ("code", "=like", "6%"), + ("account_type", "=", "expense"), + ], + limit=1, + ) + equip_account = self.env["account.account"].search( + [ + ("company_ids", "in", [main_company.id]), + ("code", "=like", "61%"), + ("account_type", "=", "expense"), + ], + limit=1, + ) + bank_account = self.env["account.account"].search( + [ + ("company_ids", "in", [main_company.id]), + ("account_type", "in", ["asset_cash", "asset_current"]), + ], + limit=1, + ) + journal = self.env["account.journal"].search( + [("type", "=", "general"), ("company_id", "=", main_company.id)], + limit=1, + ) + if not (expense_account and bank_account and journal): + _logger.warning("Could not find accounts for YourCompany demo moves") + return + Move = self.env["account.move"].with_company(main_company) + for name, amount, account in [ + ("Main Office Expenses", 18500, expense_account), + ("Main Office Equipment", 3800, equip_account or expense_account), + ]: + move = Move.create( + { + "company_id": main_company.id, + "journal_id": journal.id, + "date": "2026-03-15", + "line_ids": [ + ( + 0, + 0, + {"account_id": account.id, "debit": amount, "name": name}, + ), + ( + 0, + 0, + { + "account_id": bank_account.id, + "credit": amount, + "name": name, + }, + ), + ], + } + ) + try: + move.with_company(main_company).action_post() + except Exception: + _logger.warning("Could not post YourCompany demo move: %s", name) diff --git a/mis_builder/readme/newsfragments/784.feature b/mis_builder/readme/newsfragments/784.feature new file mode 100644 index 000000000..fbee018e1 --- /dev/null +++ b/mis_builder/readme/newsfragments/784.feature @@ -0,0 +1,3 @@ +Add ``company_id`` field to report instance periods to filter each column +to a single company, and a "Generate Company Columns" button that +auto-creates one column per company plus a Total. diff --git a/mis_builder/static/description/index.html b/mis_builder/static/description/index.html index 11ee4dac8..49b622e61 100644 --- a/mis_builder/static/description/index.html +++ b/mis_builder/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +MIS Builder -
+
+

MIS Builder

- - -Odoo Community Association - -
-

MIS Builder

-

Production/Stable License: AGPL-3 OCA/mis-builder Translate me on Weblate Try me on Runboat

+

Production/Stable License: AGPL-3 OCA/mis-builder Translate me on Weblate Try me on Runboat

This module allows you to build Management Information Systems dashboards. Such style of reports presents KPI in rows and time periods in columns. Reports mainly fetch data from account moves, but can also @@ -446,7 +441,7 @@

MIS Builder

-

Installation

+

Installation

Your preferred way to install addons will work with MIS Builder.

An easy way to install it with all its dependencies is using pip:

-

Usage

+

Usage

To configure this module, you need to:

  • Go to Accounting > Configuration > MIS Reporting > MIS Report @@ -485,7 +480,7 @@

    Usage

-

Development

+

Development

A typical extension is to provide a mechanism to filter reports on analytic dimensions or operational units. To implement this, you can override _get_additional_move_line_filter and _get_additional_filter @@ -495,7 +490,7 @@

Development

different columns to show different analytic accounts.

-

Changelog

+

Changelog

-

18.0.1.7.2 (2025-10-29)

+

18.0.1.7.2 (2025-10-29)

-

Bugfixes

+

Bugfixes

  • Fix computation of currency conversion rates (#737)
  • @@ -515,9 +510,9 @@

    Bugfixes

-

18.0.1.5.0 (2025-10-27)

+

18.0.1.5.0 (2025-10-27)

-

Features

+

Features

  • Introduction of annotations on report cells. Added notes will be pinted when exporting to PDF and Excel. @@ -526,9 +521,9 @@

    Features

-

17.0.1.0.2 (2024-11-11)

+

17.0.1.0.2 (2024-11-11)

-

Features

+

Features

  • Add support for branch companies. (#648)
  • @@ -536,7 +531,7 @@

    Features

-

16.0.5.1.9 (2024-02-09)

+

16.0.5.1.9 (2024-02-09)

Bugfixes

-

16.0.5.1.8 (2024-02-08)

+

16.0.5.1.8 (2024-02-08)

Bugfixes

-

16.0.5.1.0 (2023-04-04)

+

16.0.5.1.0 (2023-04-04)

Features

  • Improve UX by adding the option to edit the pivot date directly on the @@ -561,7 +556,7 @@

    16.0.5.1.0 (2023-04-04)

-

16.0.5.0.0 (2023-04-01)

+

16.0.5.0.0 (2023-04-01)

Features

-

15.0.4.0.5 (2022-07-19)

+

15.0.4.0.5 (2022-07-19)

Bugfixes

-

15.0.4.0.4 (2022-07-19)

+

15.0.4.0.4 (2022-07-19)

Bugfixes

-

15.0.4.0.2 (2022-02-16)

+

15.0.4.0.2 (2022-02-16)

Bugfixes

  • Fix access right issue when clicking the “Save” button on a MIS Report @@ -626,7 +621,7 @@

    15.0.4.0.2 (2022-02-16)

-

14.0.4.0.0 (2022-01-08)

+

14.0.4.0.0 (2022-01-08)

Features

-

14.0.3.6.7 (2021-06-02)

+

14.0.3.6.7 (2021-06-02)

Bugfixes

  • When on a MIS Report Instance, if you wanted to generate a new line of @@ -671,7 +666,7 @@

    14.0.3.6.7 (2021-06-02)

-

14.0.3.6.6 (2021-04-23)

+

14.0.3.6.6 (2021-04-23)

Bugfixes

  • Fix drilldown action name when the account model has been customized. @@ -679,7 +674,7 @@

    14.0.3.6.6 (2021-04-23)

-

14.0.3.6.5 (2021-04-23)

+

14.0.3.6.5 (2021-04-23)

Bugfixes

-

14.0.3.6.4 (2021-04-06)

+

14.0.3.6.4 (2021-04-06)

Features

-

13.0.3.6.3 (2020-08-28)

+

13.0.3.6.3 (2020-08-28)

Bugfixes

  • Having a “Compare columns” added on a KPI with an associated style @@ -717,7 +712,7 @@

    13.0.3.6.3 (2020-08-28)

-

13.0.3.6.2 (2020-04-22)

+

13.0.3.6.2 (2020-04-22)

Bugfixes

  • The “Settings” button is now displayed for users with the “Show full @@ -726,7 +721,7 @@

    13.0.3.6.2 (2020-04-22)

-

13.0.3.6.1 (2020-04-22)

+

13.0.3.6.1 (2020-04-22)

Bugfixes

-

13.0.3.6.0 (2020-03-28)

+

13.0.3.6.0 (2020-03-28)

Features

  • Add column-level filters on analytic account and analytic tags. These @@ -753,11 +748,11 @@

    13.0.3.6.0 (2020-03-28)

-

12.0.3.5.0 (2019-10-26)

+

12.0.3.5.0 (2019-10-26)

Features

  • The account_id field of the model selected in ‘Move lines source’ @@ -797,7 +792,7 @@

    12.0.3.5.0 (2019-10-26)

-

12.0.3.4.0 (2019-07-09)

+

12.0.3.4.0 (2019-07-09)

Features

-

12.0.3.3.0 (2019-01-26)

+

12.0.3.3.0 (2019-01-26)

Features

Dynamic analytic filters in report preview are not yet available in 11, this requires an update to the JS widget that proved difficult to @@ -873,7 +868,7 @@

12.0.3.3.0 (2019-01-26)

analytic_account_id field.

-

11.0.3.2.2 (2018-06-30)

+

11.0.3.2.2 (2018-06-30)

-

11.0.3.2.1 (2018-05-29)

+

11.0.3.2.1 (2018-05-29)

  • [FIX] Missing comparison operator for AccountingNone leading to errors in pbal computations @@ -891,7 +886,7 @@

    11.0.3.2.1 (2018-05-29)

-

10.0.3.2.0 (2018-05-02)

+

10.0.3.2.0 (2018-05-02)

-

11.0.3.1.2 (2018-02-04)

+

11.0.3.1.2 (2018-02-04)

Migration to Odoo 11. No new feature. (#67)

-

10.0.3.1.1 (2017-11-14)

+

10.0.3.1.1 (2017-11-14)

New features:

  • [ADD] month and year relative periods, easier to use than date ranges @@ -949,7 +944,7 @@

    10.0.3.1.1 (2017-11-14)

-

10.0.3.0.4 (2017-10-14)

+

10.0.3.0.4 (2017-10-14)

Bug fix:

-

10.0.3.0.3 (2017-10-03)

+

10.0.3.0.3 (2017-10-03)

Bug fix:

-

10.0.3.0.2 (2017-10-01)

+

10.0.3.0.2 (2017-10-01)

New features:

  • [ADD] Alternative move line source per report column. This makes mis @@ -1010,7 +1005,7 @@

    10.0.3.0.2 (2017-10-01)

-

10.0.2.0.3 (unreleased)

+

10.0.2.0.3 (unreleased)

  • [IMP] more robust behaviour in presence of missing expressions
  • [FIX] indent style
  • @@ -1023,7 +1018,7 @@

    10.0.2.0.3 (unreleased)

-

9.0.2.0.2 (2016-09-27)

+

9.0.2.0.2 (2016-09-27)

  • [IMP] Add refresh button in mis report preview.
  • [IMP] Widget code changes to allow to add fields in the widget more @@ -1031,7 +1026,7 @@

    9.0.2.0.2 (2016-09-27)

-

9.0.2.0.1 (2016-05-26)

+

9.0.2.0.1 (2016-05-26)

  • [IMP] remove unused argument in declare_and_compute_period() for a cleaner API. This is a breaking API changing merged in urgency before @@ -1039,7 +1034,7 @@

    9.0.2.0.1 (2016-05-26)

-

9.0.2.0.0 (2016-05-24)

+

9.0.2.0.0 (2016-05-24)

Part of the work for this release has been done at the Sorrento sprint April 26-29, 2016. The rest (ie a major refactoring) has been done in the weeks after.

@@ -1088,7 +1083,7 @@

9.0.2.0.0 (2016-05-24)

-

8.0.1.0.0 (2016-04-27)

+

8.0.1.0.0 (2016-04-27)

-

8.0.0.2.0

+

8.0.0.2.0

Pre-history. Or rather, you need to look at the git log.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -1126,15 +1121,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • ACSONE SA/NV
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -1184,6 +1179,5 @@

Maintainers

-
diff --git a/mis_builder/tests/test_mis_report_instance.py b/mis_builder/tests/test_mis_report_instance.py index 37acf04e7..3f270b658 100644 --- a/mis_builder/tests/test_mis_report_instance.py +++ b/mis_builder/tests/test_mis_report_instance.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import odoo.tests.common as common +from odoo import fields from odoo.tools import test_reports from ..models.accounting_none import AccountingNone @@ -552,6 +553,148 @@ def test_multicompany_account_code_display(self): "Account codes should not show as 'False' in multi-company reports", ) + def test_generate_company_columns(self): + """action_generate_company_columns creates one period per company + Total.""" + company2 = self.env["res.company"].create({"name": "Second Company"}) + report = self.env["mis.report"].create({"name": "MC Columns Report"}) + instance = self.env["mis.report.instance"].create( + { + "name": "MC Columns Instance", + "report_id": report.id, + "multi_company": True, + "company_ids": [ + (4, self.env.ref("base.main_company").id), + (4, company2.id), + ], + } + ) + instance.action_generate_company_columns() + periods = instance.period_ids.sorted("sequence") + # Expect 3 periods: main_company, company2, Total + self.assertEqual(len(periods), 3) + self.assertEqual(periods[0].company_id, self.env.ref("base.main_company")) + self.assertEqual(periods[1].company_id, company2) + self.assertFalse(periods[2].company_id) + self.assertEqual(periods[2].source, "sumcol") + self.assertTrue(instance.comparison_mode) + + def test_generate_company_columns_guard(self): + """action_generate_company_columns raises UserError when preconditions unmet.""" + from odoo.exceptions import UserError + + report = self.env["mis.report"].create({"name": "MC Guard Report"}) + # single-company instance (multi_company=False) + instance = self.env["mis.report.instance"].create( + {"name": "MC Guard Instance", "report_id": report.id} + ) + with self.assertRaises(UserError): + instance.action_generate_company_columns() + # multi_company=True but only 1 company selected + instance.multi_company = True + instance._onchange_company() + with self.assertRaises(UserError): + instance.action_generate_company_columns() + + def _post_expense(self, company, expense, bank, amount): + """Post one balanced expense move in ``company`` on shared accounts.""" + journal = self.env["account.journal"].create( + { + "name": "Misc", + "type": "general", + "code": "MISC", + "company_id": company.id, + } + ) + move = self.env["account.move"].create( + { + "company_id": company.id, + "journal_id": journal.id, + "date": fields.Date.today(), + "line_ids": [ + (0, 0, {"account_id": expense.id, "debit": amount, "name": "x"}), + (0, 0, {"account_id": bank.id, "credit": amount, "name": "x"}), + ], + } + ) + move.action_post() + + def test_generate_company_columns_hierarchy_no_double_count(self): + """Total sums each selected company once, even with a parent/branch tree. + + A parent company with two real branches (``parent_id`` set). Each + generated column filters on an exact ``company_id`` match, so a branch's + moves appear only in that branch's column -- never rolled up into the + parent's column. The Total must therefore equal the disjoint sum + (100 + 10 + 20 = 130), not double-count the branches (which a + ``child_of`` filter would, giving a parent column of 130 and a Total + of 160). + """ + Company = self.env["res.company"] + parent = Company.create({"name": "Acme Group"}) + branch1 = Company.create({"name": "Acme North", "parent_id": parent.id}) + branch2 = Company.create({"name": "Acme South", "parent_id": parent.id}) + companies = parent + branch1 + branch2 + # Branches share the parent's chart of accounts, so the accounts are + # created once and used by every company in the tree. + expense = ( + self.env["account.account"] + .with_company(parent) + .create( + { + "name": "Expense", + "code": "600000", + "account_type": "expense", + "company_ids": [(6, 0, companies.ids)], + } + ) + ) + bank = ( + self.env["account.account"] + .with_company(parent) + .create( + { + "name": "Bank", + "code": "101401", + "account_type": "asset_current", + "company_ids": [(6, 0, companies.ids)], + } + ) + ) + self._post_expense(parent, expense, bank, 100) + self._post_expense(branch1, expense, bank, 10) + self._post_expense(branch2, expense, bank, 20) + + report = self.env["mis.report"].create({"name": "Hierarchy Report"}) + self.env["mis.report.kpi"].create( + { + "report_id": report.id, + "name": "exp", + "description": "Expenses", + "sequence": 1, + "expression_ids": [(0, 0, {"name": "balp[600%]"})], + } + ) + instance = self.env["mis.report.instance"].create( + { + "name": "Hierarchy Instance", + "report_id": report.id, + "multi_company": True, + "company_ids": [(6, 0, companies.ids)], + } + ) + instance.action_generate_company_columns() + matrix = instance.with_context(allowed_company_ids=companies.ids).compute() + cols = [c["label"] for c in matrix["header"][0]["cols"]] + exp_row = next(r for r in matrix["body"] if r["label"] == "Expenses") + vals = { + label: cell.get("val") + for label, cell in zip(cols, exp_row["cells"], strict=False) + } + self.assertEqual(vals["Acme Group"], 100) + self.assertEqual(vals["Acme North"], 10) + self.assertEqual(vals["Acme South"], 20) + self.assertEqual(vals["Total"], 130) + def test_qweb(self): self.report_instance.print_pdf() # get action test_reports.try_report( diff --git a/mis_builder/views/mis_report_instance.xml b/mis_builder/views/mis_report_instance.xml index d1f2c7df9..795ec9f95 100644 --- a/mis_builder/views/mis_report_instance.xml +++ b/mis_builder/views/mis_report_instance.xml @@ -137,7 +137,16 @@ - + +