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.

256 lines
8.3 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
  3. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
  4. from odoo import fields, models, api, exceptions, _
  5. import logging
  6. logger = logging.getLogger('[mail_digest]')
  7. class MailDigest(models.Model):
  8. _name = 'mail.digest'
  9. _description = 'Mail digest'
  10. _order = 'create_date desc'
  11. name = fields.Char(
  12. string="Name",
  13. compute="_compute_name",
  14. readonly=True,
  15. )
  16. partner_id = fields.Many2one(
  17. string='Partner',
  18. comodel_name='res.partner',
  19. readonly=True,
  20. required=True,
  21. ondelete='cascade',
  22. )
  23. frequency = fields.Selection(
  24. related='partner_id.notify_frequency',
  25. readonly=True,
  26. )
  27. message_ids = fields.Many2many(
  28. comodel_name='mail.message',
  29. string='Messages'
  30. )
  31. mail_id = fields.Many2one(
  32. 'mail.mail',
  33. 'Mail',
  34. ondelete='set null',
  35. )
  36. state = fields.Selection(related='mail_id.state', readonly=True)
  37. template_id = fields.Many2one(
  38. 'ir.ui.view',
  39. 'Qweb mail template',
  40. ondelete='set null',
  41. default=lambda self: self._default_template_id(),
  42. domain=[('type', '=', 'qweb')],
  43. )
  44. def _default_template_id(self):
  45. """Retrieve default template to render digest."""
  46. return self.env.ref('mail_digest.default_digest_tmpl',
  47. raise_if_not_found=False)
  48. @api.multi
  49. @api.depends("partner_id", "partner_id.notify_frequency")
  50. def _compute_name(self):
  51. for rec in self:
  52. rec.name = u'{} - {}'.format(
  53. rec.partner_id.name, rec._get_subject())
  54. @api.model
  55. def create_or_update(self, partners, message, subtype_id=None):
  56. """Create or update digest.
  57. :param partners: recipients as `res.partner` browse list
  58. :param message: `mail.message` to include in digest
  59. :param subtype_id: `mail.message.subtype` instance
  60. """
  61. subtype_id = subtype_id or message.subtype_id
  62. for partner in partners:
  63. digest = self._get_or_create_by_partner(partner, message)
  64. digest.message_ids |= message
  65. return True
  66. @api.model
  67. def _get_by_partner(self, partner, mail_id=False):
  68. """Retrieve digest record for given partner.
  69. :param partner: `res.partner` browse record
  70. :param mail_id: `mail.mail` record for further filtering.
  71. By default we lookup for pending digest without notification yet.
  72. """
  73. domain = [
  74. ('partner_id', '=', partner.id),
  75. ('mail_id', '=', mail_id),
  76. ]
  77. return self.search(domain, limit=1)
  78. @api.model
  79. def _get_or_create_by_partner(self, partner, message=None, mail_id=False):
  80. """Retrieve digest record or create it by partner.
  81. :param partner: `res.partner` record to create/get digest for
  82. :param message: `mail.message` to include in digest
  83. :param mail_id: `mail.mail` record to set on digest
  84. """
  85. existing = self._get_by_partner(partner, mail_id=mail_id)
  86. if existing:
  87. return existing
  88. values = {'partner_id': partner.id, }
  89. return self.create(values)
  90. @api.model
  91. def _message_group_by_key(self, msg):
  92. """Return the key to group messages by."""
  93. return msg.subtype_id.id
  94. @api.multi
  95. def _message_group_by(self):
  96. """Group digest messages.
  97. A digest can contain several messages.
  98. To display them in a nice and organized form in your emails
  99. we group them by subtype by default.
  100. """
  101. self.ensure_one()
  102. grouped = {}
  103. for msg in self.message_ids:
  104. grouped.setdefault(self._message_group_by_key(msg), []).append(msg)
  105. return grouped
  106. def _get_site_name(self):
  107. """Retrieve site name for meaningful mail subject.
  108. If you run a website we get website's name
  109. otherwise we default to current user's company name.
  110. """
  111. # default to company
  112. name = self.env.user.company_id.name
  113. if 'website' in self.env:
  114. # TODO: shall we make this configurable at digest or global level?
  115. # Maybe you have a website but
  116. # your digest msgs are not related to it at all or partially.
  117. ws = None
  118. try:
  119. ws = self.env['website'].get_current_website()
  120. name = ws.name
  121. except RuntimeError:
  122. # RuntimeError: object unbound -> no website request.
  123. # Fallback to default website if any.
  124. ws = self.env['website'].search([], limit=1)
  125. if ws:
  126. name = ws.name
  127. return name
  128. @api.multi
  129. def _get_subject(self):
  130. """Build the full subject for digest's mail."""
  131. # TODO: shall we move this to computed field?
  132. self.ensure_one()
  133. subject = u'[{}] '.format(self._get_site_name())
  134. if self.partner_id.notify_frequency == 'daily':
  135. subject += _('Daily update')
  136. elif self.partner_id.notify_frequency == 'weekly':
  137. subject += _('Weekly update')
  138. return subject
  139. @api.multi
  140. def _get_template_values(self):
  141. """Collect variables to render digest's template."""
  142. self.ensure_one()
  143. subject = self._get_subject()
  144. template_values = {
  145. 'digest': self,
  146. 'subject': subject,
  147. 'grouped_messages': self._message_group_by(),
  148. 'base_url':
  149. self.env['ir.config_parameter'].get_param('web.base.url'),
  150. }
  151. return template_values
  152. @api.multi
  153. def _get_email_values(self, template=None):
  154. """Collect variables to create digest's mail message."""
  155. self.ensure_one()
  156. template = template or self.template_id
  157. if not template:
  158. raise exceptions.UserError(_(
  159. 'You must pass a template or set one on the digest record.'
  160. ))
  161. subject = self._get_subject()
  162. template_values = self._get_template_values()
  163. values = {
  164. 'email_from': self.env.user.company_id.email,
  165. 'recipient_ids': [(4, self.partner_id.id)],
  166. 'subject': subject,
  167. 'body_html': template.with_context(
  168. **self._template_context()
  169. ).render(template_values),
  170. }
  171. return values
  172. def _create_mail_context(self):
  173. """Inject context vars.
  174. By default we make sure that digest's email
  175. will have only digest's partner among recipients.
  176. """
  177. return {
  178. 'notify_only_recipients': True,
  179. }
  180. @api.multi
  181. def _template_context(self):
  182. """Rendering context for digest's template.
  183. By default we enforce partner's language.
  184. """
  185. self.ensure_one()
  186. return {
  187. 'lang': self.partner_id.lang,
  188. }
  189. @api.multi
  190. def create_email(self, template=None):
  191. """Create `mail.message` records for current digests.
  192. :param template: qweb template instance to override default digest one.
  193. """
  194. mail_model = self.env['mail.mail'].with_context(
  195. **self._create_mail_context())
  196. created = []
  197. for item in self:
  198. if not item.message_ids:
  199. # useless to create a mail for a digest w/ messages
  200. # messages could be deleted by admin for instance.
  201. continue
  202. values = item.with_context(
  203. **item._template_context()
  204. )._get_email_values(template=template)
  205. item.mail_id = mail_model.create(values)
  206. created.append(item.id)
  207. if created:
  208. logger.info('Create email for digest IDS=%s', str(created))
  209. return created
  210. @api.multi
  211. def action_create_email(self):
  212. return self.create_email()
  213. @api.model
  214. def process(self, frequency='daily', domain=None):
  215. """Process existing digest records to create emails via cron.
  216. :param frequency: lookup digest records by partners' `notify_frequency`
  217. :param domain: pass custom domain to lookup only specific digests
  218. """
  219. if not domain:
  220. domain = [
  221. ('mail_id', '=', False),
  222. ('partner_id.notify_frequency', '=', frequency),
  223. ]
  224. self.search(domain).create_email()