You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.7 KiB

  1. # Copyright (C) 2018 by Camptocamp
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import api, fields, models, _
  4. from datetime import datetime, timedelta
  5. import logging
  6. _logger = logging.getLogger(__name__)
  7. class IrCron(models.Model):
  8. _inherit = 'ir.cron'
  9. oneshot = fields.Boolean(
  10. string='Single use',
  11. default=False,
  12. help='Enable this to run the cron only once. '
  13. 'The cron will be deleted right after execution.'
  14. )
  15. @api.model
  16. def create(self, vals):
  17. if vals.get('oneshot'):
  18. vals.update(self._oneshot_defaults(**vals))
  19. return super().create(vals)
  20. def _oneshot_defaults(
  21. self, name=None, delay=('minutes', 10), nextcall=None, **kw):
  22. if nextcall is None:
  23. nextcall = fields.Datetime.to_string(
  24. datetime.now() + timedelta(**dict([delay, ]))
  25. )
  26. return {
  27. 'state': 'code',
  28. # TODO: shall we enforce `doall` too?
  29. # enforce numbercall
  30. 'numbercall': 1,
  31. # make sure name is automatic
  32. 'name': self._oneshot_make_name(name),
  33. 'nextcall': nextcall,
  34. }
  35. def _oneshot_make_name(self, name=None):
  36. name = ' ' + (name if name else '')
  37. return '{}{}'.format(
  38. self.env['ir.sequence'].next_by_code('cron.oneshot'), name
  39. )
  40. @api.model
  41. def schedule_oneshot(self, model_name, method=None, code=None,
  42. delay=('minutes', 10), **kw):
  43. """Create a one shot cron.
  44. :param model_name: a string matching an odoo model name
  45. :param method: an existing method to call on the model
  46. :param code: custom code to run on the model
  47. :param delay: timedelta compat values for delay as tuple
  48. :param kw: custom values for the cron
  49. """
  50. assert method or code, _('Provide a method or some code!')
  51. if method and not code:
  52. code = 'model.{}()'.format(method)
  53. model = self.env['ir.model']._get(model_name)
  54. vals = {
  55. 'model_id': model.id,
  56. 'code': code,
  57. }
  58. vals.update(self._oneshot_defaults(delay=delay))
  59. vals.update(kw)
  60. # make sure is a oneshot cron ;)
  61. vals['oneshot'] = True
  62. return self.create(vals)
  63. def _oneshot_cleanup_domain(self):
  64. # TODO: any better way to select them?
  65. return [
  66. ('oneshot', '=', True),
  67. ('numbercall', '=', 0), # already executed
  68. ('active', '=', False), # already executed and numbercall=0
  69. ]
  70. @api.model
  71. def cron_oneshot_cleanup(self):
  72. self.with_context(
  73. active_test=False
  74. ).search(self._oneshot_cleanup_domain()).unlink()