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.

190 lines
6.6 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. import logging
  5. from openerp import api, fields, models, tools
  6. _logger = logging.getLogger(__name__)
  7. BATCH_SIZE_DEFAULT = 500
  8. class MailMassMailingSending(models.Model):
  9. _name = 'mail.mass_mailing.sending'
  10. state = fields.Selection([
  11. ('draft', "Draft"),
  12. ('enqueued', "Enqueued"),
  13. ('sending', "Sending"),
  14. ('sent', "Sent"),
  15. ('error', "Error"),
  16. ], string="State", required=True, copy=False, default='enqueued')
  17. mass_mailing_id = fields.Many2one(
  18. string="Mass mailing", comodel_name='mail.mass_mailing',
  19. readonly=True, ondelete='cascade')
  20. pending_count = fields.Integer(
  21. string="Pending recipients", compute='_compute_pending_count')
  22. sending_count = fields.Integer(
  23. string="Mails to be sent", compute='_compute_sending_count')
  24. sent_count = fields.Integer(
  25. string="Sent mails", compute='_compute_sent_count')
  26. failed_count = fields.Integer(
  27. string="Failed mails", compute='_compute_failed_count')
  28. error = fields.Char(string="Error message")
  29. date_start = fields.Datetime(
  30. string="Date start", default=fields.Datetime.now())
  31. date_end = fields.Datetime(string="Date end")
  32. @api.model
  33. def batch_size_get(self):
  34. m_param = self.env['ir.config_parameter']
  35. batch_size = BATCH_SIZE_DEFAULT
  36. batch_size_str = m_param.get_param(
  37. 'mail.mass_mailing.sending.batch_size')
  38. if batch_size_str and batch_size_str.isdigit():
  39. batch_size = int(batch_size_str)
  40. return batch_size
  41. @api.multi
  42. def pending_emails(self):
  43. return self.env['mail.mail.statistics'].search([
  44. ('mass_mailing_sending_id', 'in', self.ids),
  45. ('scheduled', '!=', False),
  46. ('sent', '=', False),
  47. ('exception', '=', False),
  48. ])
  49. @api.multi
  50. def get_recipient_batch(self, res_ids):
  51. batch_size = self.batch_size_get()
  52. already_enqueued = self.env['mail.mail.statistics'].search([
  53. ('mass_mailing_sending_id', 'in', self.ids),
  54. ])
  55. set_ids = set(res_ids)
  56. new_ids = list(
  57. set_ids - set(already_enqueued.mapped('res_id')))
  58. if not self.env.context.get('sending_avoid_batch', False):
  59. new_ids = new_ids[:batch_size]
  60. if set(new_ids) != set_ids:
  61. return new_ids
  62. return res_ids
  63. @api.multi
  64. def pending_recipients(self):
  65. self.ensure_one()
  66. m_mailing = self.env['mail.mass_mailing'].with_context(
  67. mass_mailing_sending_id=self.id, sending_avoid_batch=True)
  68. return m_mailing.get_recipients(self.mass_mailing_id)
  69. @api.multi
  70. def send_mail(self):
  71. for sending in self:
  72. try:
  73. sending.with_context(mass_mailing_sending_id=sending.id).\
  74. mass_mailing_id.send_mail()
  75. except Exception as e:
  76. sending._send_error(e)
  77. return True
  78. @api.multi
  79. def _send_error(self, exception):
  80. self.ensure_one()
  81. self.write({
  82. 'error': tools.ustr(exception),
  83. 'state': 'error',
  84. 'date_end': fields.Datetime.now(),
  85. })
  86. self.mass_mailing_id.state = 'done'
  87. @api.multi
  88. def _process_enqueued(self):
  89. # Create mail_mail objects not created
  90. self.ensure_one()
  91. if self.pending_recipients():
  92. self.send_mail()
  93. # If there is no more recipient left, mark as sending
  94. if not self.pending_recipients():
  95. self.state = 'sending'
  96. self._process_sending()
  97. elif self.mass_mailing_id.state not in {'sending', 'error'}:
  98. self.mass_mailing_id.state = 'sending'
  99. @api.multi
  100. def _process_sending(self):
  101. # Check if there is any mail_mail object not sent
  102. self.ensure_one()
  103. if not self.pending_emails():
  104. self.mass_mailing_id.state = 'done'
  105. self.write({
  106. 'state': 'sent',
  107. 'date_end': fields.Datetime.now(),
  108. })
  109. elif self.mass_mailing_id.state not in {'sending', 'error'}:
  110. self.mass_mailing_id.state = 'sending'
  111. @api.multi
  112. def _process(self):
  113. self.ensure_one()
  114. method = getattr(self, '_process_%s' % self.state, None)
  115. if method and hasattr(method, '__call__'):
  116. return method()
  117. return False # pragma: no cover
  118. @api.model
  119. def sendings_running(self):
  120. return self.search([
  121. ('state', 'in', ('enqueued', 'sending')),
  122. ])
  123. @api.model
  124. def cron(self):
  125. # Process all mail.mass_mailing.sending in enqueue or sending state
  126. sendings = self.sendings_running()
  127. for sending in sendings:
  128. _logger.info("Sending [%d] mass mailing [%d] '%s' (%s)",
  129. sending.id, sending.mass_mailing_id.id,
  130. sending.mass_mailing_id.name, sending.state)
  131. # Process sending using user who created it
  132. sending = sending.sudo(user=sending.create_uid.id)
  133. ctx = sending.create_uid.context_get()
  134. sending.with_context(**ctx)._process()
  135. return True
  136. @api.multi
  137. def _compute_pending_count(self):
  138. for sending in self.filtered(lambda r: r.state == 'enqueued'):
  139. sending.pending_count = len(sending.pending_recipients())
  140. @api.multi
  141. def _compute_sending_count(self):
  142. m_stats = self.env['mail.mail.statistics']
  143. for sending in self.filtered(
  144. lambda r: r.state in {'enqueued', 'sending'}):
  145. sending.sending_count = m_stats.search_count([
  146. ('mass_mailing_sending_id', '=', sending.id),
  147. ('scheduled', '!=', False),
  148. ('sent', '=', False),
  149. ('exception', '=', False),
  150. ])
  151. @api.multi
  152. def _compute_sent_count(self):
  153. m_stats = self.env['mail.mail.statistics']
  154. for sending in self:
  155. sending.sent_count = m_stats.search_count([
  156. ('mass_mailing_sending_id', '=', sending.id),
  157. ('scheduled', '!=', False),
  158. ('sent', '!=', False),
  159. ('exception', '=', False),
  160. ])
  161. @api.multi
  162. def _compute_failed_count(self):
  163. m_stats = self.env['mail.mail.statistics']
  164. for sending in self:
  165. sending.failed_count = m_stats.search_count([
  166. ('mass_mailing_sending_id', '=', sending.id),
  167. ('scheduled', '!=', False),
  168. ('exception', '!=', False),
  169. ])