Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions compute_field_after_install/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
===========================
Compute field after install
===========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:26bad7f441b66e0de5861db7e578a3651b9e17b90748e4ca3fe6ba91025751ee
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |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%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/18.0/compute_field_after_install
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-compute_field_after_install
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Computed field computation can be a really long process when installing
a module and can block the migration process for a long time.

This module gives the possibility to defer the field computation after
the installation.

⚠️ Use with caution ⚠️

Not all computed fields can be deferred without risking the generation
of corrupted data. E.g. the total amount of an invoice, if differed,
could lead to wrong data computation in other stored fields that depend
on it. That's why this module should never be used in a database
migration process such as OpenUpgrade, where computed fields have to be
triggered by the framework as expected.

**Table of contents**

.. contents::
:local:

Usage
=====

Just install any module with computed field.

Database is available quickly whatever its size and computed fields.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/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
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20compute_field_after_install%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* Akretion

Contributors
------------

- Sébastien BEAU <sebastien.beau@akretion.com>
- Florian Mounier <florian.mounier@akretion.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-sebastienbeau| image:: https://github.com/sebastienbeau.png?size=40px
:target: https://github.com/sebastienbeau
:alt: sebastienbeau

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-sebastienbeau|

This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/compute_field_after_install>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions compute_field_after_install/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions compute_field_after_install/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2025 Akretion (http://www.akretion.com)
# Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

{
"name": "Compute field after install",
"summary": "Compute computed fields after the install process",
"version": "18.0.1.0.0",
"category": "Tools",
"website": "https://github.com/OCA/server-tools",
"author": "Akretion,Odoo Community Association (OCA)",
"maintainers": ["sebastienbeau"],
"license": "AGPL-3",
"installable": True,
"depends": [
"base",
],
"data": [
"data/ir_cron.xml",
"views/recompute_field_view.xml",
"security/ir.model.access.csv",
],
}
18 changes: 18 additions & 0 deletions compute_field_after_install/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2025 Akretion (http://www.akretion.com).
@author Sébastien BEAU <sebastien.beau@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo noupdate="1">
<record forcecreate="True" id="ir_cron_scheduler_alarm" model="ir.cron">
<field name="name">Run Recompute field</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">30</field>
<field name="interval_type">minutes</field>
<field name="model_id" ref="model_recompute_field" />
<field name="state">code</field>
<field name="code">model._run_all()</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions compute_field_after_install/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import recompute_field
137 changes: 137 additions & 0 deletions compute_field_after_install/models/recompute_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright 2025 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).


import logging
from functools import reduce
from itertools import groupby

from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.tools import config
from odoo.tools.translate import _

_logger = logging.getLogger(__name__)


class RecomputeField(models.Model):
_name = "recompute.field"
_description = "Recompute Field"

model = fields.Char(required=True)
field = fields.Char(required=True)
last_id = fields.Integer(
help="Last record ID on which computing have been executed"
)

step = fields.Integer(
required=True,
help="Recomputing batch size.",
compute="_compute_default_step",
store=True,
readonly=False,
precompute=True,
)
state = fields.Selection(
[
("todo", "Todo"),
("done", "Done"),
]
)

@api.depends("model", "field")
def _compute_default_step(self):
for recompute_field in self:
model = recompute_field.model.replace(".", "_")
recompute_field.step = config.get(
f"computed_fields_batch_size__{model}__{recompute_field.field}",
config.get(
f"computed_fields_batch_size__{model}",
config.get("computed_fields_batch_size", 1000),
),
)

@api.constrains("step")
def _check_step(self):
for recompute_field in self:
if recompute_field.step <= 0:
raise UserError(_("Step must be greater than 0"))

@api.model
def _run_all(self):
return self.search([("state", "=", "todo")]).run()

def run(self):
# Group tasks by compute method to avoid computing multifields
# computes multiple times

def group_key(task):
return task.model, str(self.env[task.model]._fields[task.field].compute)

model_tasks = groupby(
self.sorted(key=group_key),
group_key,
)
for (model, _compute_fun), tasks in model_tasks:
tasks = reduce(lambda x, y: x | y, tasks)
fields = set(tasks.mapped("field"))
last_id = max(tasks.mapped("last_id"), default=None)
step = min(tasks.mapped("step"))

while True:
_logger.info(
"Recompute fields %s for model %s in background. Last id %d",
fields,
model,
last_id,
)
records = self.env[model].search(
[("id", "<", last_id)] if last_id else [],
limit=step,
order="id desc",
)
if not records:
tasks.state = "done"
self.env.cr.commit() # pylint: disable=E8102
break
for field in fields:
field_ = records._fields[field]
self.env.add_to_compute(field_, records)

records.flush_recordset()
last_id = records[-1].id
tasks.last_id = last_id
self.env.cr.commit() # pylint: disable=E8102

return True


ori_add_to_compute = api.Environment.add_to_compute


def add_to_compute(self, field, records):
if (
"recompute.field" in self
and len(records) > config.get("computed_fields_defer_threshold", 50000)
and self.context.get("module")
and not getattr(field, "precompute", False)
):
_logger.info(
"Deferring computation of field %s for model %s as there is %s records",
field.name,
records._name,
len(records),
)
self["recompute.field"].create(
{
"field": field.name,
"model": records._name,
"state": "todo",
}
)
else:
return ori_add_to_compute(self, field, records)


api.Environment.add_to_compute = add_to_compute
3 changes: 3 additions & 0 deletions compute_field_after_install/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
20 changes: 20 additions & 0 deletions compute_field_after_install/readme/CONFIGURATION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This module can be installed just like any other Odoo module, by adding it
to Odoo *addons_path*. In order for the module to work correctly,
it needs to be loaded as a server-wide module.
This can be done with the ``server_wide_modules`` parameter in your Odoo config
file or with the ``--load`` command-line parameter.

Additionnaly you can customize the minimum number of records that will defer the
fields computation by adding the following variable in the odoo config file
``computed_fields_defer_threshold=20000``

You can also customize the batch size of the fields computation by adding
the following variable in the odoo config file
``computed_fields_batch_size=1000`` or more specifically for a model
``computed_fields_batch_size__<model_name>=1000`` and for a specific model field
``computed_fields_batch_size__<model_name>__<field_name>=1000``

i.e.:
``computed_fields_batch_size__res_partner=2000``
``computed_fields_batch_size__res_partner__commercial_company_name=50``

2 changes: 2 additions & 0 deletions compute_field_after_install/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Sébastien BEAU \<sebastien.beau@akretion.com\>
- Florian Mounier \<florian.mounier@akretion.com\>
14 changes: 14 additions & 0 deletions compute_field_after_install/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Computed field computation can be a really long process when installing
a module and can block the migration process for a long time.

This module gives the possibility to defer the field computation after
the installation.

:warning: Use with caution :warning:

Not all computed fields can be deferred without risking the generation of
corrupted data. E.g. the total amount of an invoice, if differed, could lead
to wrong data computation in other stored fields that depend on it. That's why
this module should never be used in a database migration process such as
OpenUpgrade, where computed fields have to be triggered by the framework
as expected.
3 changes: 3 additions & 0 deletions compute_field_after_install/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Just install any module with computed field.

Database is available quickly whatever its size and computed fields.
2 changes: 2 additions & 0 deletions compute_field_after_install/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_recompute_field,access_recompute_field,model_recompute_field,base.group_user,1,0,0,0
Loading
Loading