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.

231 lines
8.2 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 models, fields, api, _
  5. class ResPartner(models.Model):
  6. _inherit = 'res.partner'
  7. notify_email = fields.Selection(selection_add=[('digest', _('Digest'))])
  8. notify_frequency = fields.Selection(
  9. string='Frequency',
  10. selection=[
  11. ('daily', 'Daily'),
  12. ('weekly', 'Weekly')
  13. ],
  14. default='weekly',
  15. required=True,
  16. )
  17. notify_conf_ids = fields.One2many(
  18. string='Notifications',
  19. inverse_name='partner_id',
  20. comodel_name='partner.notification.conf',
  21. )
  22. enabled_notify_subtype_ids = fields.Many2many(
  23. string='Partner enabled subtypes',
  24. comodel_name='mail.message.subtype',
  25. compute='_compute_enabled_notify_subtype_ids',
  26. search='_search_enabled_notify_subtype_ids',
  27. )
  28. disabled_notify_subtype_ids = fields.Many2many(
  29. string='Partner disabled subtypes',
  30. comodel_name='mail.message.subtype',
  31. compute='_compute_disabled_notify_subtype_ids',
  32. search='_search_disabled_notify_subtype_ids',
  33. )
  34. @api.multi
  35. def _compute_notify_subtypes(self, enabled):
  36. self.ensure_one()
  37. query = (
  38. 'SELECT subtype_id FROM partner_notification_conf '
  39. 'WHERE partner_id=%s AND enabled = %s'
  40. )
  41. self.env.cr.execute(
  42. query, (self.id, enabled))
  43. return [x[0] for x in self.env.cr.fetchall()]
  44. @api.multi
  45. @api.depends('notify_conf_ids.subtype_id')
  46. def _compute_enabled_notify_subtype_ids(self):
  47. for partner in self:
  48. partner.enabled_notify_subtype_ids = \
  49. partner._compute_notify_subtypes(True)
  50. @api.multi
  51. @api.depends('notify_conf_ids.subtype_id')
  52. def _compute_disabled_notify_subtype_ids(self):
  53. for partner in self:
  54. partner.disabled_notify_subtype_ids = \
  55. partner._compute_notify_subtypes(False)
  56. def _search_notify_subtype_ids_domain(self, operator, value, enabled):
  57. """Build domain to search notification subtypes by partner settings."""
  58. if operator in ('in', 'not in') and \
  59. not isinstance(value, (tuple, list)):
  60. value = [value, ]
  61. conf_value = value
  62. if isinstance(conf_value, int):
  63. # we search conf records always w/ 'in'
  64. conf_value = [conf_value]
  65. _value = self.env['partner.notification.conf'].search([
  66. ('subtype_id', 'in', conf_value),
  67. ('enabled', '=', enabled),
  68. ]).mapped('partner_id').ids
  69. return [('id', operator, _value)]
  70. def _search_enabled_notify_subtype_ids(self, operator, value):
  71. return self._search_notify_subtype_ids_domain(
  72. operator, value, True)
  73. def _search_disabled_notify_subtype_ids(self, operator, value):
  74. return self._search_notify_subtype_ids_domain(
  75. operator, value, False)
  76. @api.multi
  77. def _notify(self, message,
  78. force_send=False, send_after_commit=True, user_signature=True):
  79. """Override to delegate domain generation."""
  80. # notify_by_email
  81. email_domain = self._get_notify_by_email_domain(message)
  82. # `sudo` from original odoo method
  83. # the reason should be that anybody can write messages to a partner
  84. # and you really want to find all ppl to be notified
  85. partners = self.sudo().search(email_domain)
  86. partners._notify_by_email(
  87. message, force_send=force_send,
  88. send_after_commit=send_after_commit, user_signature=user_signature)
  89. # notify_by_digest
  90. digest_domain = self._get_notify_by_email_domain(
  91. message, digest=True)
  92. partners = self.sudo().search(digest_domain)
  93. partners._notify_by_digest(message)
  94. # notify_by_chat
  95. self._notify_by_chat(message)
  96. return True
  97. def _digest_enabled_message_types(self):
  98. """Return a list of enabled message types for digest.
  99. In `_notify_by_digest` we check if digest mode is enabled
  100. for given message's type. Here we retrieve global settings
  101. from a config param that you can customize to second your needs.
  102. """
  103. param = self.env['ir.config_parameter'].sudo().get_param(
  104. 'mail_digest.enabled_message_types', default='')
  105. return [x.strip() for x in param.split(',') if x.strip()]
  106. @api.multi
  107. def _notify_by_digest(self, message):
  108. message_sudo = message.sudo()
  109. if message_sudo.message_type \
  110. not in self._digest_enabled_message_types():
  111. return
  112. self.env['mail.digest'].sudo().create_or_update(self, message)
  113. @api.model
  114. def _get_notify_by_email_domain(self, message, digest=False):
  115. """Return domain to collect partners to be notified by email.
  116. :param message: instance of mail.message
  117. :param digest: include/exclude digest enabled partners
  118. NOTE: since mail.mail inherits from mail.message
  119. this method is called even when
  120. we create the final email for mail.digest object.
  121. Here we introduce a new context flag `notify_only_recipients`
  122. to explicitely retrieve only partners among message's recipients.
  123. """
  124. message_sudo = message.sudo()
  125. channels = message.channel_ids.filtered(
  126. lambda channel: channel.email_send)
  127. email = message_sudo.author_id \
  128. and message_sudo.author_id.email or message.email_from
  129. ids = self.ids
  130. if self.env.context.get('notify_only_recipients'):
  131. ids = [x for x in ids if x in message.partner_ids.ids]
  132. domain = [
  133. '|',
  134. ('id', 'in', ids),
  135. ('channel_ids', 'in', channels.ids),
  136. ('email', '!=', email)
  137. ]
  138. if not digest:
  139. domain.append(('notify_email', 'not in', ('none', 'digest')))
  140. else:
  141. domain.append(('notify_email', '=', 'digest'))
  142. if message.subtype_id:
  143. domain.extend(self._get_domain_subtype_leaf(message.subtype_id))
  144. return domain
  145. @api.model
  146. def _get_domain_subtype_leaf(self, subtype):
  147. return [
  148. '|',
  149. ('disabled_notify_subtype_ids', 'not in', (subtype.id, )),
  150. ('enabled_notify_subtype_ids', 'in', (subtype.id, )),
  151. ]
  152. @api.multi
  153. def _notify_update_subtype(self, subtype, enable):
  154. """Update notification settings by subtype.
  155. :param subtype: `mail.message.subtype` to enable or disable
  156. :param enable: boolean to enable or disable given subtype
  157. """
  158. self.ensure_one()
  159. exists = self.env['partner.notification.conf'].search([
  160. ('subtype_id', '=', subtype.id),
  161. ('partner_id', '=', self.id)
  162. ], limit=1)
  163. if exists:
  164. exists.enabled = enable
  165. else:
  166. self.write({
  167. 'notify_conf_ids': [
  168. (0, 0, {'enabled': enable, 'subtype_id': subtype.id})]
  169. })
  170. @api.multi
  171. def _notify_enable_subtype(self, subtype):
  172. """Enable given subtype."""
  173. self._notify_update_subtype(subtype, True)
  174. @api.multi
  175. def _notify_disable_subtype(self, subtype):
  176. """Disable given subtype."""
  177. self._notify_update_subtype(subtype, False)
  178. class PartnerNotificationConf(models.Model):
  179. """Hold partner's single notification configuration."""
  180. _name = 'partner.notification.conf'
  181. _description = 'Partner notification configuration'
  182. # TODO: add friendly onchange to not yield errors when editin via UI
  183. _sql_constraints = [
  184. ('unique_partner_subtype_conf',
  185. 'unique (partner_id,subtype_id)',
  186. 'You can have only one configuration per subtype!')
  187. ]
  188. partner_id = fields.Many2one(
  189. string='Partner',
  190. comodel_name='res.partner',
  191. readonly=True,
  192. required=True,
  193. ondelete='cascade',
  194. index=True,
  195. )
  196. subtype_id = fields.Many2one(
  197. 'mail.message.subtype',
  198. 'Notification type',
  199. ondelete='cascade',
  200. required=True,
  201. index=True,
  202. )
  203. enabled = fields.Boolean(default=True, index=True)