@@ -899,3 +899,75 @@ def decorate(func):
899899 return func
900900
901901 return decorate
902+
903+
904+ def job_auto_delay (func = None , default_channel = "root" , retry_pattern = None ):
905+ """Decorator to automatically delay as job method when called
906+
907+ The decorator applies ``odoo.addons.queue_job.job`` at the same time,
908+ so the decorated method is listed in job functions. The arguments
909+ are the same, propagated to the ``job`` decorator.
910+
911+ When a method is decorated by ``job_auto_delay``, any call to the method
912+ will not directly execute the method's body, but will instead enqueue a
913+ job.
914+
915+ The options of the job usually passed to ``with_delay()`` (priority,
916+ description, identity_key, ...) can be returned in a dictionary by a method
917+ named after the name of the method suffixed by ``_job_options`` which takes
918+ the same parameters as the initial method.
919+
920+ It is still possible to directly execute the method by setting a key
921+ ``_job_force_sync`` to True in the environment context.
922+
923+ Example:
924+
925+ .. code-block:: python
926+
927+ class ProductProduct(models.Model):
928+ _inherit = 'product.product'
929+
930+ def foo_job_options(self, arg1):
931+ return {
932+ "priority": 100,
933+ "description": "Saying hello to {}".format(arg1)
934+ }
935+
936+ @job_auto_delay(default_channel="root.channel1")
937+ def foo(self, arg1):
938+ print("hello", arg1)
939+
940+ def button_x(self):
941+ foo("world")
942+
943+ The result when ``button_x`` is called, is that a new job for ``foo`` is
944+ delayed.
945+
946+ """
947+ if func is None :
948+ return functools .partial (
949+ job_auto_delay , default_channel = default_channel , retry_pattern = retry_pattern
950+ )
951+
952+ def auto_delay (self , * args , ** kwargs ):
953+ if self .env .context .get ("job_uuid" ) or self .env .context .get ("_job_force_sync" ):
954+ # we are in the job execution
955+ return func (self , * args , ** kwargs )
956+ else :
957+ # replace the synchronous call by a job on itself
958+ method_name = func .__name__
959+ job_options_method = getattr (
960+ self , "{}_job_options" .format (method_name ), None
961+ )
962+ job_options = {}
963+ if job_options_method :
964+ job_options .update (job_options_method (* args , ** kwargs ))
965+ else :
966+ job_options = {}
967+ delayed = self .with_delay (** job_options )
968+ getattr (delayed , method_name )(* args , ** kwargs )
969+
970+ return functools .update_wrapper (
971+ auto_delay ,
972+ job (func , default_channel = default_channel , retry_pattern = retry_pattern ),
973+ )
0 commit comments