Skip to content

[20.0][MIG] mis_builder: Migration to 20.0 - #842

Open
mileo wants to merge 7 commits into
OCA:20.0from
kmee:20.0-mig-mis_builder
Open

mileo wants to merge 7 commits into
OCA:20.0from
kmee:20.0-mig-mis_builder

Conversation

@mileo

@mileo mileo commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Migration from 19.0 on the existing 20.0 branch, where the module was set uninstallable.

Changes for 20.0:

  • access rights and the multi-company rule converted to ir.access.csv;
  • pytz replaced by zoneinfo in _utc_midnight (same result on ambiguous and missing times);
  • <> domain operator replaced by !=; report_file removed from the xlsx report action; compile_codeobj and try_report imports updated;
  • widget and annotation dialog ported to OWL 3 (useProps, proxy, this. in templates);
  • button icons use Odoo 20 icon names;
  • fixes for the ruff rules enabled by the 20.0 template; readme with the 20.0 menus and a screenshot;
  • name fields of the report children set copy=True, since 20.0 adds " (copy)" to Char fields named name;
  • pre-migration that removes the 19.0 xmlids of ir.model.access and ir.rule, so the upgrade can create the ir.access records.

Depends on OCA/reporting-engine#1203 (report_xlsx) and OCA/server-ux#1341 (date_range).

Installs and passes its tests (100) on Odoo 20.0 with no warnings in the log.

@cav-adhoc cav-adhoc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of the 19.0 → 20.0 migration. Python side looks fine (I checked _utc_midnight against pytz on all zones, no differences). Comments below are about the upgrade path and the Owl 3 port.

@@ -0,0 +1,25 @@
id,name,model_id,group_id/id,operation,domain
manage_mis_report_kpi,manage_mis_report_kpi,mis.report.kpi,account.group_account_manager,crud,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a fresh 20.0 database with the 19.0 xmlids pre-seeded (ir.model.access / ir.rule rows in ir_model_data), installing this module fails when loading ir.access.csv:

For external id mis_builder.manage_mis_report_kpi when trying to create/update a record of model ir.access
found record of different model ir.model.access (59942)

(6 of 6 xmlids fail.) I could not run a real 19→20 upgrade, so I don't know whether the core upgrade scripts already convert those rows. Has anyone upgraded a database with this module installed?

If the rows survive, proposed fix: add mis_builder/migrations/20.0.1.0.0/pre-migration.py that drops the stale xmlids, only the ones still pointing to the legacy models, so it is a no-op if core already converted them:

def migrate(cr, version):
    cr.execute(
        """
        DELETE FROM ir_model_data
        WHERE module = 'mis_builder'
          AND model IN ('ir.model.access', 'ir.rule')
        """
    )

I have not run this script against a real upgrade.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the pre-migration you proposed, unchanged in substance. I could not run a real 19.0 to 20.0 upgrade either, so I simulated it: install on 20.0, delete the ir.access rows, point the 24 xmlids back to ir.model.access / ir.rule and set the installed version to 19.0, then run -u mis_builder. Without the script it fails with the same "different model" error. With it the upgrade passes and the 24 xmlids are recreated as ir.access. If the core upgrade already converts them, the script does nothing.

access_mis_report_instance_period_sum,access_mis_report_instance_period_sum,mis.report.instance.period.sum,base.group_user,r,
manage_mis_report_instance,manage_mis_report_instance,mis.report.instance,account.group_account_manager,crud,
access_mis_report_instance,access_mis_report_instance,mis.report.instance,base.group_user,r,
mis_builder_multi_company_rule,Mis Report Instance multi company,mis.report.instance,,crud,"['|', ('company_id', '=', False), ('company_id', 'in', company_ids), '|', ('company_ids', '=', False), ('company_ids', 'in', company_ids)]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above, for the global rule: this xmlid currently points to an ir.rule record, and the pre-migration.py proposed in the previous comment covers it (model IN ('ir.model.access', 'ir.rule')).

Also, this is a global rule (no group) with crud; I assume that is the intended equivalent of the old rule, which applied to every operation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The old rule had no groups and the default perm_* all True, so a global ir.access with crud and the same domain is the equivalent. After the simulated upgrade it is the only restriction on mis.report.instance, with operation crud.

rows='4'
placeholder="Insert note here"
t-model="state.annotationText"
t-custom-model="this.state.annotationText"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t-custom-model is the temporary Owl 2 bridge. The compatibility layer says to replace it with t-model + signals once the code builds:
owl3_compatibility_layer.js#L29

It works today because the layer is loaded in the bundles. Core already uses the native form, for example the emoji picker:

Proposed fix for the dialog: replace the proxy state with a signal and bind it natively.

import {Component, signal, t, useProps} from "@odoo/owl";
...
setup() {
    this.annotationText = signal(this.props.annotationText, {type: t.string()});
}
<textarea ... t-model="this.annotationText" />

and wherever the dialog reads this.state.annotationText, read this.annotationText() instead. The same applies to useSubEnv imported from @web/owl2/utils in mis_report_widget.esm.js: it is only a re-export of owl.useSubEnv (utils.js#L16), so it can be imported from @odoo/owl directly. I have not run this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, following the emoji picker: annotationText = signal(this.props.annotationText, {type: t.string()}), t-model="this.annotationText", and confirm() reads this.annotationText(). useSubEnv now comes from @odoo/owl.

@MiquelRForgeFlow MiquelRForgeFlow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Odoo 20.0, Char fields named "name" are copied with a " (copy)" suffix unless the field sets copy explicitly. Copying a report failed ("KPI name (k1 (copy)) must be a valid python identifier") and copied expressions are broken.

Comment thread mis_builder/models/mis_report.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = fields.Char(required=True, copy=True)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, applied. mis.report keeps its copy() override, so the report name still gets " (copy)" and kpis, subkpis, queries, expressions, subreports and period labels keep their names. In 20.0 this comes from Char._get_attrs, which sets copy=mark_as_copy("name") on fields named name unless copy is given. Added your test plus a subreport copy test and a period label check.

Comment thread mis_builder/models/mis_report.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = fields.Char(required=True, copy=True)

Comment thread mis_builder/models/mis_report.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = fields.Char(string="Expression", copy=True)

Comment thread mis_builder/models/mis_report.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = fields.Char(required=True, copy=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = fields.Char(required=True, string="Label", translate=True, copy=True)

Also in name of mis.report.subreport.


def test_copy_mis_report_instance(self):
new_instance = self.report_instance.copy()
self.assertEqual(new_instance.name, f"{self.report_instance.name} (copy)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_copy_mis_report(self):
new_report = self.report.copy()
self.assertEqual(new_report.name, f"{self.report.name} (copy)")
self.assertEqual(
new_report.kpi_ids.mapped("name"), self.report.kpi_ids.mapped("name")
)
self.assertEqual(
new_report.subkpi_ids.mapped("name"),
self.report.subkpi_ids.mapped("name"),
)
self.assertEqual(
new_report.query_ids.mapped("name"), self.report.query_ids.mapped("name")
)
self.assertEqual(
new_report.kpi_ids.expression_ids.mapped("name"),
self.report.kpi_ids.expression_ids.mapped("name"),
)

mileo added 4 commits October 20, 2026 18:32
Assisted-by: Claude Code
Assisted-by: Claude Code
ruff DTZ005 flags datetime.now() without tz in docs/conf.py, which
makes pre-commit fail on the whole repository.

Assisted-by: Claude Code
@mileo
mileo force-pushed the 20.0-mig-mis_builder branch from 4df3b88 to 2155655 Compare October 20, 2026 22:21
@mileo

mileo commented Oct 20, 2026

Copy link
Copy Markdown
Member Author

Thanks @cav-adhoc and @MiquelRForgeFlow. Pushed:

  • copy=True on the name of mis.report.kpi, mis.report.kpi.expression, mis.report.subkpi, mis.report.query, mis.report.subreport and mis.report.instance.period. mis.report and mis.report.instance still get " (copy)". Added test_copy_mis_report, a subreport copy test and a check on the period labels in the instance copy test.
  • migrations/20.0.1.0.0/pre-migration.py that drops the mis_builder xmlids still pointing to ir.model.access / ir.rule.
  • Annotation dialog uses a signal with t-model, and useSubEnv is imported from @odoo/owl.

Tests: 100 passing on Odoo 20.0, no warnings in the log.

@cav-adhoc cav-adhoc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@cav-adhoc

Copy link
Copy Markdown

One more comment: mis_builder/static/src/components/mis_report_widget.xml still uses Font Awesome classes (fa fa-refresh, fa fa-print, fa fa-download, fa fa-cog and btn fa fa-trash-o at line 181). Odoo 20.0 no longer loads the Font Awesome stylesheet (odoo/odoo#256840), so these icons are not drawn. They should move to Material Symbols with class="oi" data-icon="..." (for example refresh, print, download, settings, delete).

Odoo 20.0 no longer loads the Font Awesome stylesheet, so the fa classes of the refresh, print, export, settings, resize and annotation delete buttons were not drawn. They use the oi class with data-icon, as the core does; the resize button switches between fullscreen and fullscreen_exit.
@mileo

mileo commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

Obrigado @cav-adhoc, ajustado: os cinco botões do widget e o botão de redimensionar (que alternava fa-expand e fa-compress pelo JS) agora usam class="oi" com data-icon. Empurrei o commit.

@cav-adhoc

Copy link
Copy Markdown

Hi @mileo, thanks a lot for the changes! 🙌

It looks like the only thing left is pre-commit: prettier wants to reformat mis_builder/static/src/components/mis_report_widget.xml. The new <i>/<span> tags with data-icon are a bit too long, so it splits the attributes into several lines. Just formatting, no code changes. This should do it:

pre-commit run --all-files
git add mis_builder/static/src/components/mis_report_widget.xml
git commit --amend --no-edit
git push --force-with-lease

Some context on why this PR is a bit of a key one right now: 20.0 uses a newer ruff (v0.16.8) that enables RUF012 by default, so it flags every _inherit = [...] in Odoo models. Odoo itself ignores that rule, and the OCA template already does too (OCA/oca-addons-repo-template#376). That fix reaches this repo with #852, but #852 can't go green until 20.0 pre-commit is fixed, and your PR fixes exactly that (the exclude list and docs/conf.py).

So once this one is merged, #852 can follow, and then #853 (mis_builder_budget) without needing any noqa. Thanks again! 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants