From e59e7897c9d1161dbdf7966b532c5d496f623aa6 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 19 Feb 2026 15:55:48 +0100 Subject: [PATCH 1/8] Add 'Allow Commit' option on job functions It is forbidden to commit inside a job, because it releases the job lock and can cause it to start again, while still being run, by the dead jobs requeuer. For some use cases, it may actually be legitimate, or at least be needed in the short term before actual updates in the code. A new option on the job function, false by default, allow to run the job in a new transaction, at the cost of an additional connection + transaction overhead. Related to #889 --- queue_job/controllers/main.py | 5 ++-- queue_job/job.py | 26 ++++++++++++++++---- queue_job/models/queue_job_function.py | 10 +++++++- queue_job/tests/common.py | 2 +- queue_job/tests/test_model_job_function.py | 2 ++ queue_job/views/queue_job_function_views.xml | 2 ++ 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index c98f6305df..c1dfbceb22 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -38,8 +38,9 @@ def _prevent_commit(cr): def forbidden_commit(*args, **kwargs): raise RuntimeError( "Commit is forbidden in queue jobs. " - "If the current job is a cron running as queue job, " - "modify it to run as a normal cron." + 'You may want to enable the "Allow Commit" option on the Job ' + "Function. Alternatively, if the current job is a cron running as " + "queue job, you can modify it to run as a normal cron." ) original_commit = cr.commit diff --git a/queue_job/job.py b/queue_job/job.py index b6cb190355..d5f1f4420f 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -8,6 +8,7 @@ import sys import uuid import weakref +from contextlib import contextmanager, nullcontext from datetime import datetime, timedelta from random import randint @@ -406,10 +407,6 @@ def __init__( self.method_name = func.__name__ self.recordset = recordset - self.env = env - self.job_model = self.env["queue.job"] - self.job_model_name = "queue.job" - self.job_config = ( self.env["queue.job.function"].sudo().job_config(self.job_function_name) ) @@ -487,7 +484,12 @@ def perform(self): """ self.retry += 1 try: - self.result = self.func(*tuple(self.args), **self.kwargs) + if self.job_config.allow_commit: + env_context_manager = self._with_temporary_env() + else: + env_context_manager = nullcontext() + with env_context_manager: + self.result = self.func(*tuple(self.args), **self.kwargs) except RetryableJobError as err: if err.ignore_retry: self.retry -= 1 @@ -507,6 +509,16 @@ def perform(self): return self.result + @contextmanager + def _with_temporary_env(self): + with self.env.registry.cursor() as new_cr: + env = self.recordset.env + self.recordset = self.recordset.with_env(env(cr=new_cr)) + try: + yield + finally: + self.recordset = self.recordset.with_env(env) + def _get_common_dependent_jobs_query(self): return """ UPDATE queue_job @@ -665,6 +677,10 @@ def __hash__(self): def db_record(self): return self.db_records_from_uuids(self.env, [self.uuid]) + @property + def env(self): + return self.recordset.env + @property def func(self): recordset = self.recordset.with_context(job_uuid=self.uuid) diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py index 7cf73ea370..f9c21c9801 100644 --- a/queue_job/models/queue_job_function.py +++ b/queue_job/models/queue_job_function.py @@ -28,7 +28,8 @@ class QueueJobFunction(models.Model): "related_action_enable " "related_action_func_name " "related_action_kwargs " - "job_function_id ", + "job_function_id " + "allow_commit", ) def _default_channel(self): @@ -79,6 +80,11 @@ def _default_channel(self): "enable, func_name, kwargs.\n" "See the module description for details.", ) + allow_commit = fields.Boolean( + help="Allows the job to commit transactions during execution. " + "Under the hood, this executes the job in a new database cursor, " + "which incurs a slight overhead.", + ) @api.depends("model_id.model", "method") def _compute_name(self): @@ -149,6 +155,7 @@ def job_default_config(self): related_action_func_name=None, related_action_kwargs={}, job_function_id=None, + allow_commit=False, ) def _parse_retry_pattern(self): @@ -184,6 +191,7 @@ def job_config(self, name): related_action_func_name=config.related_action.get("func_name"), related_action_kwargs=config.related_action.get("kwargs", {}), job_function_id=config.id, + allow_commit=config.allow_commit, ) def _retry_pattern_format_error_message(self): diff --git a/queue_job/tests/common.py b/queue_job/tests/common.py index ec036bd639..c504cd8258 100644 --- a/queue_job/tests/common.py +++ b/queue_job/tests/common.py @@ -276,7 +276,7 @@ def _add_job(self, *args, **kwargs): def _prepare_context(self, job): # pylint: disable=context-overridden - job_model = job.job_model.with_context({}) + job_model = job.env["queue.job"].with_context({}) field_records = job_model._fields["records"] # Filter the context to simulate store/load of the job job.recordset = field_records.convert_to_write(job.recordset, job_model) diff --git a/queue_job/tests/test_model_job_function.py b/queue_job/tests/test_model_job_function.py index 84676fdb65..9095f2a55e 100644 --- a/queue_job/tests/test_model_job_function.py +++ b/queue_job/tests/test_model_job_function.py @@ -42,6 +42,7 @@ def test_function_job_config(self): ' "func_name": "related_action_foo",' ' "kwargs": {"b": 1}}' ), + "allow_commit": True, } ) self.assertEqual( @@ -53,5 +54,6 @@ def test_function_job_config(self): related_action_func_name="related_action_foo", related_action_kwargs={"b": 1}, job_function_id=job_function.id, + allow_commit=True, ), ) diff --git a/queue_job/views/queue_job_function_views.xml b/queue_job/views/queue_job_function_views.xml index e725920b2c..26192cc649 100644 --- a/queue_job/views/queue_job_function_views.xml +++ b/queue_job/views/queue_job_function_views.xml @@ -10,6 +10,7 @@ + @@ -24,6 +25,7 @@ + From b4f3bec9ba7516635c3e8992da724499f708285e Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 19 Feb 2026 17:31:37 +0100 Subject: [PATCH 2/8] Add parameter to allow commit by default in jobs False on new databases, True on existing databases. Should always be False by default on future versions. --- queue_job/__manifest__.py | 1 + queue_job/data/ir_config_parameter_data.xml | 7 +++++++ .../migrations/18.0.2.2.0/post-migration.py | 13 +++++++++++++ queue_job/models/queue_job_function.py | 16 +++++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 queue_job/data/ir_config_parameter_data.xml create mode 100644 queue_job/migrations/18.0.2.2.0/post-migration.py diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py index e8360a1800..dc531dea75 100644 --- a/queue_job/__manifest__.py +++ b/queue_job/__manifest__.py @@ -21,6 +21,7 @@ "views/queue_job_menus.xml", "data/queue_data.xml", "data/queue_job_function_data.xml", + "data/ir_config_parameter_data.xml", ], "assets": { "web.assets_backend": [ diff --git a/queue_job/data/ir_config_parameter_data.xml b/queue_job/data/ir_config_parameter_data.xml new file mode 100644 index 0000000000..1cfdcd19bc --- /dev/null +++ b/queue_job/data/ir_config_parameter_data.xml @@ -0,0 +1,7 @@ + + + + queue_job.allow_commit_by_default + False + + diff --git a/queue_job/migrations/18.0.2.2.0/post-migration.py b/queue_job/migrations/18.0.2.2.0/post-migration.py new file mode 100644 index 0000000000..216fe49a8d --- /dev/null +++ b/queue_job/migrations/18.0.2.2.0/post-migration.py @@ -0,0 +1,13 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + if not version: + return + + env["ir.config_parameter"].sudo().set_param( + "queue_job.allow_commit_by_default", True + ) + env["queue.job.function"].search([]).write({"allow_commit": True}) diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py index f9c21c9801..102bcb14e7 100644 --- a/queue_job/models/queue_job_function.py +++ b/queue_job/models/queue_job_function.py @@ -7,6 +7,7 @@ from collections import namedtuple from odoo import _, api, exceptions, fields, models, tools +from odoo.tools import str2bool from ..fields import JobSerialized @@ -81,6 +82,7 @@ def _default_channel(self): "See the module description for details.", ) allow_commit = fields.Boolean( + default=lambda self: self._default_allow_commit_by_default(), help="Allows the job to commit transactions during execution. " "Under the hood, this executes the job in a new database cursor, " "which incurs a slight overhead.", @@ -155,7 +157,19 @@ def job_default_config(self): related_action_func_name=None, related_action_kwargs={}, job_function_id=None, - allow_commit=False, + allow_commit=self._default_allow_commit_by_default(), + ) + + @api.model + def _default_allow_commit_by_default(self): + # We shoud not allow commit by default on job functions, this parameter + # is here for backward compatibility, a migration sets it by default on + # existing databases, but new databases will have it set to False by + # default. + return str2bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("queue_job.allow_commit_by_default") ) def _parse_retry_pattern(self): From 6f50739c6c8c92b9ef101c93871e542822a80a22 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 20 Feb 2026 15:15:18 +0100 Subject: [PATCH 3/8] Revert "Add parameter to allow commit by default in jobs" This reverts commit b4f3bec9ba7516635c3e8992da724499f708285e. --- queue_job/__manifest__.py | 1 - queue_job/data/ir_config_parameter_data.xml | 7 ------- .../migrations/18.0.2.2.0/post-migration.py | 13 ------------- queue_job/models/queue_job_function.py | 16 +--------------- 4 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 queue_job/data/ir_config_parameter_data.xml delete mode 100644 queue_job/migrations/18.0.2.2.0/post-migration.py diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py index dc531dea75..e8360a1800 100644 --- a/queue_job/__manifest__.py +++ b/queue_job/__manifest__.py @@ -21,7 +21,6 @@ "views/queue_job_menus.xml", "data/queue_data.xml", "data/queue_job_function_data.xml", - "data/ir_config_parameter_data.xml", ], "assets": { "web.assets_backend": [ diff --git a/queue_job/data/ir_config_parameter_data.xml b/queue_job/data/ir_config_parameter_data.xml deleted file mode 100644 index 1cfdcd19bc..0000000000 --- a/queue_job/data/ir_config_parameter_data.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - queue_job.allow_commit_by_default - False - - diff --git a/queue_job/migrations/18.0.2.2.0/post-migration.py b/queue_job/migrations/18.0.2.2.0/post-migration.py deleted file mode 100644 index 216fe49a8d..0000000000 --- a/queue_job/migrations/18.0.2.2.0/post-migration.py +++ /dev/null @@ -1,13 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) -from openupgradelib import openupgrade - - -@openupgrade.migrate() -def migrate(env, version): - if not version: - return - - env["ir.config_parameter"].sudo().set_param( - "queue_job.allow_commit_by_default", True - ) - env["queue.job.function"].search([]).write({"allow_commit": True}) diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py index 102bcb14e7..f9c21c9801 100644 --- a/queue_job/models/queue_job_function.py +++ b/queue_job/models/queue_job_function.py @@ -7,7 +7,6 @@ from collections import namedtuple from odoo import _, api, exceptions, fields, models, tools -from odoo.tools import str2bool from ..fields import JobSerialized @@ -82,7 +81,6 @@ def _default_channel(self): "See the module description for details.", ) allow_commit = fields.Boolean( - default=lambda self: self._default_allow_commit_by_default(), help="Allows the job to commit transactions during execution. " "Under the hood, this executes the job in a new database cursor, " "which incurs a slight overhead.", @@ -157,19 +155,7 @@ def job_default_config(self): related_action_func_name=None, related_action_kwargs={}, job_function_id=None, - allow_commit=self._default_allow_commit_by_default(), - ) - - @api.model - def _default_allow_commit_by_default(self): - # We shoud not allow commit by default on job functions, this parameter - # is here for backward compatibility, a migration sets it by default on - # existing databases, but new databases will have it set to False by - # default. - return str2bool( - self.env["ir.config_parameter"] - .sudo() - .get_param("queue_job.allow_commit_by_default") + allow_commit=False, ) def _parse_retry_pattern(self): From 3b0619e54ffa8ec1bdeaf24427bfffec942bee48 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 20 Feb 2026 15:16:52 +0100 Subject: [PATCH 4/8] Improve documentation on allow commit --- queue_job/controllers/main.py | 3 ++- queue_job/models/queue_job_function.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index c1dfbceb22..056542eac5 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -40,7 +40,8 @@ def forbidden_commit(*args, **kwargs): "Commit is forbidden in queue jobs. " 'You may want to enable the "Allow Commit" option on the Job ' "Function. Alternatively, if the current job is a cron running as " - "queue job, you can modify it to run as a normal cron." + "queue job, you can modify it to run as a normal cron. More details on: " + "https://github.com/OCA/queue/wiki/%5BDRAFT%5D-Upgrade-warning:-commits-inside-jobs" ) original_commit = cr.commit diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py index f9c21c9801..edf90c9ab7 100644 --- a/queue_job/models/queue_job_function.py +++ b/queue_job/models/queue_job_function.py @@ -83,7 +83,8 @@ def _default_channel(self): allow_commit = fields.Boolean( help="Allows the job to commit transactions during execution. " "Under the hood, this executes the job in a new database cursor, " - "which incurs a slight overhead.", + "which incurs an overhead as it requires an extra connection to " + "the database. " ) @api.depends("model_id.model", "method") From b7e0c83cf63f198fd3c30a63a6b4c352dd93e280 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 24 Feb 2026 11:35:38 +0100 Subject: [PATCH 5/8] Fix missing job.env setter As the controller changes env on Job instances. --- queue_job/controllers/main.py | 4 +++- queue_job/job.py | 11 +++++++---- queue_job/tests/test_run_rob_controller.py | 6 ++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index 056542eac5..7a1d83537b 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -15,6 +15,7 @@ from odoo import SUPERUSER_ID, _, api, http from odoo.modules.registry import Registry from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY +from odoo.tools import config from ..delay import chain, group from ..exception import FailedJobError, RetryableJobError @@ -105,7 +106,8 @@ def _try_perform_job(cls, env, job): job.set_done() job.store() env.flush_all() - env.cr.commit() + if not config["test_enable"]: + env.cr.commit() _logger.debug("%s done", job) @classmethod diff --git a/queue_job/job.py b/queue_job/job.py index d5f1f4420f..89cdf31561 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -403,7 +403,6 @@ def __init__( raise TypeError("Job accepts only methods of Models") recordset = func.__self__ - env = recordset.env self.method_name = func.__name__ self.recordset = recordset @@ -456,10 +455,10 @@ def __init__( self.exc_message = None self.exc_info = None - if "company_id" in env.context: - company_id = env.context["company_id"] + if "company_id" in self.env.context: + company_id = self.env.context["company_id"] else: - company_id = env.company.id + company_id = self.env.company.id self.company_id = company_id self._eta = None self.eta = eta @@ -681,6 +680,10 @@ def db_record(self): def env(self): return self.recordset.env + @env.setter + def env(self, env): + self.recordset = self.recordset.with_env(env) + @property def func(self): recordset = self.recordset.with_context(job_uuid=self.uuid) diff --git a/queue_job/tests/test_run_rob_controller.py b/queue_job/tests/test_run_rob_controller.py index bb63bc82ec..1a15f4363a 100644 --- a/queue_job/tests/test_run_rob_controller.py +++ b/queue_job/tests/test_run_rob_controller.py @@ -15,3 +15,9 @@ def test_get_failure_values(self): self.assertEqual( rslt, {"exc_info": "info", "exc_name": "Exception", "exc_message": "zero"} ) + + def test_runjob_success(self): + job = self.env["queue.job"].with_delay()._test_job() + RunJobController._runjob(self.env, job) + self.assertEqual(job.state, "done") + self.assertEqual(job.db_record().state, "done") From bd2b1ff0db09e554da6e422f200aa665e469276e Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 24 Feb 2026 16:00:20 +0100 Subject: [PATCH 6/8] Add failure retry in queue job test job --- queue_job/controllers/main.py | 13 +++++++++++++ queue_job/models/queue_job.py | 19 ++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index 7a1d83537b..c4614b5db2 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -233,6 +233,7 @@ def create_test_job( failure_rate=0, job_duration=0, commit_within_job=False, + failure_retry_seconds=0, ): if not http.request.env.user.has_group("base.group_erp_manager"): raise Forbidden(_("Access Denied")) @@ -270,6 +271,12 @@ def create_test_job( except ValueError: max_retries = None + if failure_retry_seconds is not None: + try: + failure_retry_seconds = int(failure_retry_seconds) + except ValueError: + failure_retry_seconds = 0 + if size == 1: return self._create_single_test_job( priority=priority, @@ -279,6 +286,7 @@ def create_test_job( failure_rate=failure_rate, job_duration=job_duration, commit_within_job=commit_within_job, + failure_retry_seconds=failure_retry_seconds, ) if size > 1: @@ -291,6 +299,7 @@ def create_test_job( failure_rate=failure_rate, job_duration=job_duration, commit_within_job=commit_within_job, + failure_retry_seconds=failure_retry_seconds, ) return "" @@ -304,6 +313,7 @@ def _create_single_test_job( failure_rate=0, job_duration=0, commit_within_job=False, + failure_retry_seconds=0, ): delayed = ( http.request.env["queue.job"] @@ -317,6 +327,7 @@ def _create_single_test_job( failure_rate=failure_rate, job_duration=job_duration, commit_within_job=commit_within_job, + failure_retry_seconds=failure_retry_seconds, ) ) return f"job uuid: {delayed.db_record().uuid}" @@ -333,6 +344,7 @@ def _create_graph_test_jobs( failure_rate=0, job_duration=0, commit_within_job=False, + failure_retry_seconds=0, ): model = http.request.env["queue.job"] current_count = 0 @@ -359,6 +371,7 @@ def _create_graph_test_jobs( failure_rate=failure_rate, job_duration=job_duration, commit_within_job=commit_within_job, + failure_retry_seconds=failure_retry_seconds, ) ) diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py index 3d550bf33b..09851e2734 100644 --- a/queue_job/models/queue_job.py +++ b/queue_job/models/queue_job.py @@ -12,7 +12,7 @@ from odoo.addons.base_sparse_field.models.fields import Serialized from ..delay import Graph -from ..exception import JobError +from ..exception import JobError, RetryableJobError from ..fields import JobSerialized from ..job import ( CANCELLED, @@ -453,10 +453,23 @@ def related_action_open_record(self): ) return action - def _test_job(self, failure_rate=0, job_duration=0, commit_within_job=False): + def _test_job( + self, + failure_rate=0, + job_duration=0, + commit_within_job=False, + failure_retry_seconds=0, + ): _logger.info("Running test job.") if random.random() <= failure_rate: - raise JobError("Job failed") + if failure_retry_seconds: + raise RetryableJobError( + f"Retryable job failed, will be retried in " + f"{failure_retry_seconds} seconds", + seconds=failure_retry_seconds, + ) + else: + raise JobError("Job failed") if job_duration: time.sleep(job_duration) if commit_within_job: From 33ca9e78c645ad9c044d07d7088587eac5a30f58 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 24 Feb 2026 16:00:50 +0100 Subject: [PATCH 7/8] Simplify job env management --- queue_job/controllers/main.py | 8 +++----- queue_job/job.py | 14 +++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index c4614b5db2..e744971ee0 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -13,7 +13,6 @@ from werkzeug.exceptions import BadRequest, Forbidden from odoo import SUPERUSER_ID, _, api, http -from odoo.modules.registry import Registry from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY from odoo.tools import config @@ -145,8 +144,7 @@ def _enqueue_dependent_jobs(cls, env, job): def _runjob(cls, env: api.Environment, job: Job) -> None: def retry_postpone(job, message, seconds=None): job.env.clear() - with Registry(job.env.cr.dbname).cursor() as new_cr: - job.env = api.Environment(new_cr, SUPERUSER_ID, {}) + with job.in_temporary_env(): job.postpone(result=message, seconds=seconds) job.set_pending(reset_retry=False) job.store() @@ -171,6 +169,7 @@ def retry_postpone(job, message, seconds=None): # traceback in the logs we should have the traceback when all # retries are exhausted env.cr.rollback() + return except (FailedJobError, Exception) as orig_exception: buff = StringIO() @@ -178,8 +177,7 @@ def retry_postpone(job, message, seconds=None): traceback_txt = buff.getvalue() _logger.error(traceback_txt) job.env.clear() - with Registry(job.env.cr.dbname).cursor() as new_cr: - job.env = job.env(cr=new_cr) + with job.in_temporary_env(): vals = cls._get_failure_values(job, traceback_txt, orig_exception) job.set_failed(**vals) job.store() diff --git a/queue_job/job.py b/queue_job/job.py index 89cdf31561..e890558734 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -484,7 +484,7 @@ def perform(self): self.retry += 1 try: if self.job_config.allow_commit: - env_context_manager = self._with_temporary_env() + env_context_manager = self.in_temporary_env() else: env_context_manager = nullcontext() with env_context_manager: @@ -509,14 +509,14 @@ def perform(self): return self.result @contextmanager - def _with_temporary_env(self): + def in_temporary_env(self): with self.env.registry.cursor() as new_cr: - env = self.recordset.env - self.recordset = self.recordset.with_env(env(cr=new_cr)) + env = self.env + self._env = env(cr=new_cr) try: yield finally: - self.recordset = self.recordset.with_env(env) + self._env = env def _get_common_dependent_jobs_query(self): return """ @@ -681,7 +681,7 @@ def env(self): return self.recordset.env @env.setter - def env(self, env): + def _env(self, env): self.recordset = self.recordset.with_env(env) @property @@ -748,7 +748,7 @@ def model_name(self): @property def user_id(self): - return self.recordset.env.uid + return self.env.uid @property def eta(self): From 7ae3a60a93193486ecd2e02c047c336c43e63e73 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 27 Feb 2026 08:56:23 +0100 Subject: [PATCH 8/8] Update upgrade warning link --- queue_job/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py index e744971ee0..afd6ce0de0 100644 --- a/queue_job/controllers/main.py +++ b/queue_job/controllers/main.py @@ -41,7 +41,7 @@ def forbidden_commit(*args, **kwargs): 'You may want to enable the "Allow Commit" option on the Job ' "Function. Alternatively, if the current job is a cron running as " "queue job, you can modify it to run as a normal cron. More details on: " - "https://github.com/OCA/queue/wiki/%5BDRAFT%5D-Upgrade-warning:-commits-inside-jobs" + "https://github.com/OCA/queue/wiki/Upgrade-warning:-commits-inside-jobs" ) original_commit = cr.commit