Browse Source

[MIG][11.0] mail_digest

pull/228/head
Simone Orsi 6 years ago
committed by Holger Brunn
parent
commit
6a576a5427
  1. 15
      mail_digest/README.rst
  2. 9
      mail_digest/__manifest__.py
  3. 15
      mail_digest/data/config_param.xml
  4. 46
      mail_digest/data/ir_cron.xml
  5. 39
      mail_digest/demo/ir_ui_view.xml
  6. 10
      mail_digest/demo/mail_template.xml
  7. 1
      mail_digest/models/__init__.py
  8. 64
      mail_digest/models/mail_digest.py
  9. 169
      mail_digest/models/res_partner.py
  10. 113
      mail_digest/models/res_users.py
  11. 33
      mail_digest/models/user_notification_conf.py
  12. 4
      mail_digest/security/ir.model.access.csv
  13. 8
      mail_digest/security/record_rules.xml
  14. 2
      mail_digest/templates/digest_default.xml
  15. 156
      mail_digest/tests/test_digest.py
  16. 156
      mail_digest/tests/test_partner_domains.py
  17. 119
      mail_digest/tests/test_subtypes_conf.py
  18. 4
      mail_digest/views/mail_digest_views.xml
  19. 38
      mail_digest/views/partner_views.xml
  20. 28
      mail_digest/views/user_notification_views.xml
  21. 23
      mail_digest/views/user_views.xml

15
mail_digest/README.rst

@ -31,9 +31,11 @@ all the messages are collected inside a `mail.digest` container.
A daily cron and a weekly cron will take care of creating a single email per each digest,
which will be sent as a standard email.
If the message has a specific subtype, all of this will work only
if personal settings allow to receive notification for that specific subtype.
Specifically:
**Rules**
Given that the user has `Notification management = Handle by Emails`...
a message with subtype assigned *will be sent* via digest if:
* no record for type: message passes
* record disabled for type: message don't pass
@ -42,6 +44,11 @@ Specifically:
NOTE: under the hood the digest notification logic excludes followers to be notified,
since you really want to notify only mail.digest's partner.
a message with subtype assigned *will NOT be sent* via digest if:
* global: `mail_digest_enabled_message_types` param disables the message type
* user: digest mode is OFF for the recipient
* user: recipient's user has disabled the subtype in her/his settings
Global settings
---------------
@ -66,7 +73,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
help us smash it by providing a detailed and welcomed feedback.
Credits
=======

9
mail_digest/__manifest__.py

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
{
'name': 'Mail digest',
'summary': """Basic digest mail handling.""",
'version': '10.0.1.0.1',
'version': '11.0.1.0.0',
'license': 'AGPL-3',
'author': 'Camptocamp,Odoo Community Association (OCA)',
'author': 'Camptocamp, Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/social',
'depends': [
'mail',
@ -18,7 +17,7 @@
'security/ir.model.access.csv',
'security/record_rules.xml',
'views/mail_digest_views.xml',
'views/partner_views.xml',
'views/user_notification_views.xml',
'views/user_views.xml',
'templates/digest_default.xml',
],

15
mail_digest/data/config_param.xml

@ -1,9 +1,8 @@
<odoo>
<data noupdate="1">
<record id="mail_digest_enabled_message_types" model="ir.config_parameter">
<field name="key">mail_digest.enabled_message_types</field>
<!-- enabled by default for each message type -->
<field name="value">email,notification,comment</field>
</record>
</data>
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="mail_digest_enabled_message_types" model="ir.config_parameter">
<field name="key">mail_digest.enabled_message_types</field>
<!-- enabled by default for each message type -->
<field name="value">email,notification,comment</field>
</record>
</odoo>

46
mail_digest/data/ir_cron.xml

@ -1,27 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_mail_digest_daily_action" model="ir.cron">
<field name="name">Digest mail process - daily</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'mail.digest'" name="model"/>
<field eval="'process'" name="function"/>
<field eval="'()'" name="args"/>
</record>
<record forcecreate="True" id="ir_cron_mail_digest_weekly_action" model="ir.cron">
<field name="name">Digest mail process - weekly</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">weeks</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'mail.digest'" name="model"/>
<field eval="'process'" name="function"/>
<field eval="'(\'weekly\',)'" name="args"/>
</record>
</data>
<odoo noupdate="1">
<record forcecreate="True" id="ir_cron_mail_digest_daily_action" model="ir.cron">
<field name="name">Digest mail process - daily</field>
<field name="model_id" ref="model_mail_digest"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field name="code">model.process()</field>
</record>
<record forcecreate="True" id="ir_cron_mail_digest_weekly_action" model="ir.cron">
<field name="name">Digest mail process - weekly</field>
<field name="model_id" ref="model_mail_digest"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">weeks</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="code">model.process(frequency='weekly')</field>
</record>
</odoo>

39
mail_digest/demo/ir_ui_view.xml

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="view_email_template_corporate_identity">
<body>
<html>
<img style="float: right" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
<!-- if some template calling us sets this variable,
we print a h1 tag /-->
<h1 t-if="email_heading"><t t-esc="email_heading" /></h1>
<t t-raw="0" />
<!-- use some standard footer if the user doesn't have
a signature /-->
<footer t-if="not email_use_user_signature">
<p>
<a t-att-href="env.user.company_id.website">
<t t-esc="env.user.company_id.name" />
</a>
</p>
<p><t t-esc="env.user.company_id.phone" /></p>
</footer>
<footer t-if="email_use_user_signature">
<t t-raw="env.user.signature" />
</footer>
</html>
</body>
</template>
<template id="view_email_template_demo1">
<!-- because we can simply call the ci here, we don't need to
repeat it /-->
<t t-call="email_template_qweb.view_email_template_corporate_identity">
<!-- the template we call uses this as title if we set it /-->
<t t-set="email_heading" t-value="email_template.subject" />
<h2>Dear <t t-esc="object.name" />,</h2>
<p>
This is an email template using qweb.
</p>
</t>
</template>
</odoo>

10
mail_digest/demo/mail_template.xml

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="email_template_demo1" model="mail.template">
<field name="name">QWeb demo</field>
<field name="body_type">qweb</field>
<field name="body_view_id" ref="view_email_template_demo1" />
<field name="model_id" ref="base.model_res_users" />
<field name="subject">QWeb demo email</field>
</record>
</odoo>

1
mail_digest/models/__init__.py

@ -1,3 +1,4 @@
from . import mail_digest
from . import user_notification_conf
from . import res_partner
from . import res_users

64
mail_digest/models/mail_digest.py

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import fields, models, api, exceptions, _
@ -19,15 +18,15 @@ class MailDigest(models.Model):
compute="_compute_name",
readonly=True,
)
partner_id = fields.Many2one(
string='Partner',
comodel_name='res.partner',
user_id = fields.Many2one(
string='User',
comodel_name='res.users',
readonly=True,
required=True,
ondelete='cascade',
)
frequency = fields.Selection(
related='partner_id.notify_frequency',
related='user_id.digest_frequency',
readonly=True,
)
message_ids = fields.Many2many(
@ -52,7 +51,6 @@ class MailDigest(models.Model):
ondelete='set null',
default=lambda self: self._default_digest_template_id(),
domain=[('type', '=', 'qweb')],
oldname='template_id',
)
def _default_digest_template_id(self):
@ -61,53 +59,47 @@ class MailDigest(models.Model):
raise_if_not_found=False)
@api.multi
@api.depends("partner_id", "partner_id.notify_frequency")
@api.depends("user_id", "user_id.digest_frequency")
def _compute_name(self):
for rec in self:
rec.name = u'{} - {}'.format(
rec.partner_id.name, rec._get_subject())
rec.name = '{} - {}'.format(
rec.user_id.name, rec._get_subject())
@api.model
def create_or_update(self, partners, message, subtype_id=None):
def create_or_update(self, partners, message):
"""Create or update digest.
:param partners: recipients as `res.partner` browse list
:param message: `mail.message` to include in digest
:param subtype_id: `mail.message.subtype` instance
"""
subtype_id = subtype_id or message.subtype_id
for partner in partners:
digest = self._get_or_create_by_partner(partner, message)
digest = self._get_or_create_by_user(partner.real_user_id)
digest.message_ids |= message
return True
@api.model
def _get_by_partner(self, partner, mail_id=False):
"""Retrieve digest record for given partner.
def _get_by_user(self, user):
"""Retrieve digest record for given user.
:param partner: `res.partner` browse record
:param mail_id: `mail.mail` record for further filtering.
:param user: `res.users` browse record
By default we lookup for pending digest without notification yet.
"""
domain = [
('partner_id', '=', partner.id),
('mail_id', '=', mail_id),
('user_id', '=', user.id),
]
return self.search(domain, limit=1)
@api.model
def _get_or_create_by_partner(self, partner, message=None, mail_id=False):
"""Retrieve digest record or create it by partner.
def _get_or_create_by_user(self, user):
"""Retrieve digest record or create it by user.
:param partner: `res.partner` record to create/get digest for
:param message: `mail.message` to include in digest
:param mail_id: `mail.mail` record to set on digest
:param user: `res.users` record to create/get digest for
"""
existing = self._get_by_partner(partner, mail_id=mail_id)
existing = self._get_by_user(user)
if existing:
return existing
values = {'partner_id': partner.id, }
values = {'user_id': user.id, }
return self.create(values)
@api.model
@ -158,10 +150,10 @@ class MailDigest(models.Model):
"""Build the full subject for digest's mail."""
# TODO: shall we move this to computed field?
self.ensure_one()
subject = u'[{}] '.format(self._get_site_name())
if self.partner_id.notify_frequency == 'daily':
subject = '[{}] '.format(self._get_site_name())
if self.user_id.digest_frequency == 'daily':
subject += _('Daily update')
elif self.partner_id.notify_frequency == 'weekly':
elif self.user_id.digest_frequency == 'weekly':
subject += _('Weekly update')
return subject
@ -192,7 +184,7 @@ class MailDigest(models.Model):
template_values = self._get_template_values()
values = {
'email_from': self.env.user.company_id.email,
'recipient_ids': [(4, self.partner_id.id)],
'recipient_ids': [(4, self.user_id.partner_id.id)],
'subject': subject,
'body_html': template.with_context(
**self._template_context()
@ -204,7 +196,7 @@ class MailDigest(models.Model):
"""Inject context vars.
By default we make sure that digest's email
will have only digest's partner among recipients.
will have only digest's user among recipients.
"""
return {
'notify_only_recipients': True,
@ -214,11 +206,11 @@ class MailDigest(models.Model):
def _template_context(self):
"""Rendering context for digest's template.
By default we enforce partner's language.
By default we enforce user's language.
"""
self.ensure_one()
return {
'lang': self.partner_id.lang,
'lang': self.user_id.lang,
}
@api.multi
@ -252,12 +244,12 @@ class MailDigest(models.Model):
def process(self, frequency='daily', domain=None):
"""Process existing digest records to create emails via cron.
:param frequency: lookup digest records by partners' `notify_frequency`
:param frequency: lookup digest records by users' `digest_frequency`
:param domain: pass custom domain to lookup only specific digests
"""
if not domain:
domain = [
('mail_id', '=', False),
('partner_id.notify_frequency', '=', frequency),
('user_id.digest_frequency', '=', frequency),
]
self.search(domain).create_email()

169
mail_digest/models/res_partner.py

@ -1,88 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import models, fields, api, _
from odoo import models, api
class ResPartner(models.Model):
_inherit = 'res.partner'
notify_email = fields.Selection(selection_add=[('digest', _('Digest'))])
notify_frequency = fields.Selection(
string='Frequency',
selection=[
('daily', 'Daily'),
('weekly', 'Weekly')
],
default='weekly',
required=True,
)
notify_conf_ids = fields.One2many(
string='Notifications',
inverse_name='partner_id',
comodel_name='partner.notification.conf',
)
enabled_notify_subtype_ids = fields.Many2many(
string='Partner enabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_enabled_notify_subtype_ids',
search='_search_enabled_notify_subtype_ids',
)
disabled_notify_subtype_ids = fields.Many2many(
string='Partner disabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_disabled_notify_subtype_ids',
search='_search_disabled_notify_subtype_ids',
)
@api.multi
def _compute_notify_subtypes(self, enabled):
self.ensure_one()
query = (
'SELECT subtype_id FROM partner_notification_conf '
'WHERE partner_id=%s AND enabled = %s'
)
self.env.cr.execute(
query, (self.id, enabled))
return [x[0] for x in self.env.cr.fetchall()]
@api.multi
@api.depends('notify_conf_ids.subtype_id')
def _compute_enabled_notify_subtype_ids(self):
for partner in self:
partner.enabled_notify_subtype_ids = \
partner._compute_notify_subtypes(True)
@api.multi
@api.depends('notify_conf_ids.subtype_id')
def _compute_disabled_notify_subtype_ids(self):
for partner in self:
partner.disabled_notify_subtype_ids = \
partner._compute_notify_subtypes(False)
def _search_notify_subtype_ids_domain(self, operator, value, enabled):
"""Build domain to search notification subtypes by partner settings."""
if operator in ('in', 'not in') and \
not isinstance(value, (tuple, list)):
value = [value, ]
conf_value = value
if isinstance(conf_value, int):
# we search conf records always w/ 'in'
conf_value = [conf_value]
_value = self.env['partner.notification.conf'].search([
('subtype_id', 'in', conf_value),
('enabled', '=', enabled),
]).mapped('partner_id').ids
return [('id', operator, _value)]
def _search_enabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(
operator, value, True)
def _search_disabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(
operator, value, False)
# Shortcut to bypass this weird thing of odoo:
# `partner.user_id` is the "saleman"
# while the user is stored into `user_ids`
# but in the majority of the cases we have one real user per partner.
@property
def real_user_id(self):
return self.user_ids[0] if self.user_ids else False
@api.multi
def _notify(self, message,
@ -94,17 +25,14 @@ class ResPartner(models.Model):
# the reason should be that anybody can write messages to a partner
# and you really want to find all ppl to be notified
partners = self.sudo().search(email_domain)
partners._notify_by_email(
super(ResPartner, partners)._notify(
message, force_send=force_send,
send_after_commit=send_after_commit, user_signature=user_signature)
send_after_commit=send_after_commit,
user_signature=user_signature)
# notify_by_digest
digest_domain = self._get_notify_by_email_domain(
message, digest=True)
digest_domain = self._get_notify_by_email_domain(message, digest=True)
partners = self.sudo().search(digest_domain)
partners._notify_by_digest(message)
# notify_by_chat
self._notify_by_chat(message)
return True
def _digest_enabled_message_types(self):
@ -153,12 +81,10 @@ class ResPartner(models.Model):
'|',
('id', 'in', ids),
('channel_ids', 'in', channels.ids),
('email', '!=', email)
('email', '!=', email),
('user_ids.digest_mode', '=', digest),
('user_ids.notification_type', '=', 'email'),
]
if not digest:
domain.append(('notify_email', 'not in', ('none', 'digest')))
else:
domain.append(('notify_email', '=', 'digest'))
if message.subtype_id:
domain.extend(self._get_domain_subtype_leaf(message.subtype_id))
return domain
@ -167,65 +93,6 @@ class ResPartner(models.Model):
def _get_domain_subtype_leaf(self, subtype):
return [
'|',
('disabled_notify_subtype_ids', 'not in', (subtype.id, )),
('enabled_notify_subtype_ids', 'in', (subtype.id, )),
('user_ids.disabled_notify_subtype_ids', 'not in', (subtype.id, )),
('user_ids.enabled_notify_subtype_ids', 'in', (subtype.id, )),
]
@api.multi
def _notify_update_subtype(self, subtype, enable):
"""Update notification settings by subtype.
:param subtype: `mail.message.subtype` to enable or disable
:param enable: boolean to enable or disable given subtype
"""
self.ensure_one()
exists = self.env['partner.notification.conf'].search([
('subtype_id', '=', subtype.id),
('partner_id', '=', self.id)
], limit=1)
if exists:
exists.enabled = enable
else:
self.write({
'notify_conf_ids': [
(0, 0, {'enabled': enable, 'subtype_id': subtype.id})]
})
@api.multi
def _notify_enable_subtype(self, subtype):
"""Enable given subtype."""
self._notify_update_subtype(subtype, True)
@api.multi
def _notify_disable_subtype(self, subtype):
"""Disable given subtype."""
self._notify_update_subtype(subtype, False)
class PartnerNotificationConf(models.Model):
"""Hold partner's single notification configuration."""
_name = 'partner.notification.conf'
_description = 'Partner notification configuration'
# TODO: add friendly onchange to not yield errors when editin via UI
_sql_constraints = [
('unique_partner_subtype_conf',
'unique (partner_id,subtype_id)',
'You can have only one configuration per subtype!')
]
partner_id = fields.Many2one(
string='Partner',
comodel_name='res.partner',
readonly=True,
required=True,
ondelete='cascade',
index=True,
)
subtype_id = fields.Many2one(
'mail.message.subtype',
'Notification type',
ondelete='cascade',
required=True,
index=True,
)
enabled = fields.Boolean(default=True, index=True)

113
mail_digest/models/res_users.py

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import models
from odoo import models, fields, api
class Users(models.Model):
@ -18,11 +17,111 @@ class Users(models.Model):
[copied from mail.models.users]
"""
super(Users, self).__init__(pool, cr)
new_fields = [
'digest_mode',
'digest_frequency',
'notify_conf_ids',
]
# duplicate list to avoid modifying the original reference
type(self).SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
type(self).SELF_WRITEABLE_FIELDS.extend(['notify_frequency'])
type(self).SELF_WRITEABLE_FIELDS.extend(['notify_conf_ids'])
type(self).SELF_WRITEABLE_FIELDS.extend(new_fields)
# duplicate list to avoid modifying the original reference
type(self).SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS)
type(self).SELF_READABLE_FIELDS.extend(['notify_frequency'])
type(self).SELF_READABLE_FIELDS.extend(['notify_conf_ids'])
type(self).SELF_READABLE_FIELDS.extend(new_fields)
digest_mode = fields.Boolean(
default=False,
help='If enabled, email notifications will be sent in digest mode.'
)
digest_frequency = fields.Selection(
string='Frequency',
selection=[
('daily', 'Daily'),
('weekly', 'Weekly')
],
default='weekly',
required=True,
)
notify_conf_ids = fields.One2many(
string='Notifications',
inverse_name='user_id',
comodel_name='user.notification.conf',
)
enabled_notify_subtype_ids = fields.Many2many(
string='User enabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_notify_subtype_ids',
search='_search_enabled_notify_subtype_ids',
)
disabled_notify_subtype_ids = fields.Many2many(
string='User disabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_notify_subtype_ids',
search='_search_disabled_notify_subtype_ids',
)
def _notify_subtypes_by_state(self, enabled):
self.ensure_one()
return self.notify_conf_ids.filtered(
lambda x: x.enabled == enabled).mapped('subtype_id')
@api.multi
@api.depends('notify_conf_ids.subtype_id', 'notify_conf_ids.enabled')
def _compute_notify_subtype_ids(self):
for rec in self:
rec.enabled_notify_subtype_ids = \
rec._notify_subtypes_by_state(True)
rec.disabled_notify_subtype_ids = \
rec._notify_subtypes_by_state(False)
def _search_notify_subtype_ids_domain(self, operator, value, enabled):
"""Build domain to search notification subtypes by user conf."""
if operator in ('in', 'not in') and \
not isinstance(value, (tuple, list)):
value = [value, ]
conf_value = value
if isinstance(conf_value, int):
# we search conf records always w/ 'in'
conf_value = [conf_value]
_value = self.env['user.notification.conf'].search([
('subtype_id', 'in', conf_value),
('enabled', '=', enabled),
]).mapped('user_id').ids
return [('id', operator, _value)]
def _search_enabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(operator, value, True)
def _search_disabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(operator, value, False)
def _notify_update_subtype(self, subtype, enable):
"""Update notification settings by subtype.
:param subtype: `mail.message.subtype` to enable or disable
:param enable: boolean to enable or disable given subtype
"""
self.ensure_one()
exists = self.env['user.notification.conf'].search([
('subtype_id', '=', subtype.id),
('user_id', '=', self.id)
], limit=1)
if exists:
exists.enabled = enable
else:
self.write({
'notify_conf_ids': [
(0, 0, {'enabled': enable, 'subtype_id': subtype.id})]
})
@api.multi
def _notify_enable_subtype(self, subtype):
"""Enable given subtype."""
for rec in self:
rec._notify_update_subtype(subtype, True)
@api.multi
def _notify_disable_subtype(self, subtype):
"""Disable given subtype."""
for rec in self:
rec._notify_update_subtype(subtype, False)

33
mail_digest/models/user_notification_conf.py

@ -0,0 +1,33 @@
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import models, fields
class UserNotificationConf(models.Model):
"""Hold user's single notification configuration."""
_name = 'user.notification.conf'
_description = 'User notification configuration'
# TODO: add friendly onchange to not yield errors when editin via UI
_sql_constraints = [
('unique_user_subtype_conf',
'unique (user_id,subtype_id)',
'You can have only one configuration per subtype!')
]
user_id = fields.Many2one(
string='User',
comodel_name='res.users',
readonly=True,
required=True,
ondelete='cascade',
index=True,
)
subtype_id = fields.Many2one(
'mail.message.subtype',
'Notification type',
ondelete='cascade',
required=True,
index=True,
)
enabled = fields.Boolean(default=True, index=True)

4
mail_digest/security/ir.model.access.csv

@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_partner_notification_conf_user,partner.notification.user,model_partner_notification_conf,base.group_user,1,1,1,1
access_partner_notification_conf_user,partner.notification.user,model_user_notification_conf,base.group_user,1,1,1,1
access_mail_digest_system,mail.digest.sys,model_mail_digest,base.group_system,1,1,1,1
access_partner_notification_conf_system,partner.notification.sys,model_partner_notification_conf,base.group_system,1,1,1,1
access_partner_notification_conf_system,partner.notification.sys,model_user_notification_conf,base.group_system,1,1,1,1

8
mail_digest/security/record_rules.xml

@ -1,14 +1,14 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.rule" id="partner_notification_conf_owner">
<field name="name">Partners can edit their own notification settings</field>
<field name="model_id" ref="model_partner_notification_conf"/>
<record model="ir.rule" id="user_notification_conf_owner">
<field name="name">Users can edit their own notification settings</field>
<field name="model_id" ref="model_user_notification_conf"/>
<field name="perm_read" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="domain_force">['|',('partner_id', '=', user.partner_id.id), ('create_uid', '=', user.id)]</field>
<field name="domain_force">['|',('user_id', '=', user.id), ('create_uid', '=', user.id)]</field>
</record>
</odoo>

2
mail_digest/templates/digest_default.xml

@ -26,7 +26,7 @@
<div id="mail_content">
<h2>Hello,</h2>
<div id="mail_inner_content">
<t t-foreach="grouped_messages.iterkeys()" t-as="gkey">
<t t-foreach="grouped_messages.keys()" t-as="gkey">
<t t-set="messages" t-value="grouped_messages[gkey]" />
<t t-foreach="messages" t-as="msg">
<div style="margin:20px">

156
mail_digest/tests/test_digest.py

@ -1,59 +1,58 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.tests.common import TransactionCase
from odoo.tests.common import SavepointCase
from odoo import exceptions
class DigestCase(TransactionCase):
def setUp(self):
super(DigestCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
self.digest_model = self.env['mail.digest']
self.conf_model = self.env['partner.notification.conf']
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2',
'email': 'partner2@test.foo.com',
})
self.partner3 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 3',
'email': 'partner3@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
class DigestCase(SavepointCase):
@classmethod
def setUpClass(cls):
super(DigestCase, cls).setUpClass()
cls.message_model = cls.env['mail.message']
cls.subtype_model = cls.env['mail.message.subtype']
cls.digest_model = cls.env['mail.digest']
cls.conf_model = cls.env['user.notification.conf']
user_model = cls.env['res.users'].with_context(
no_reset_password=True, tracking_disable=True)
cls.user1 = user_model.create({
'name': 'User 1',
'login': 'testuser1',
'email': 'testuser1@email.com',
})
cls.user2 = user_model.create({
'name': 'User 2',
'login': 'testuser2',
'email': 'testuser2@email.com',
})
cls.user3 = user_model.create({
'name': 'User 3',
'login': 'testuser3',
'email': 'testuser3@email.com',
})
cls.subtype1 = cls.subtype_model.create({'name': 'Type 1'})
cls.subtype2 = cls.subtype_model.create({'name': 'Type 2'})
def test_get_or_create_digest(self):
message1 = self.message_model.create({
self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
})
message2 = self.message_model.create({
self.message_model.create({
'body': 'My Body 2',
'subtype_id': self.subtype2.id,
})
# 2 messages, 1 digest container
dig1 = self.digest_model._get_or_create_by_partner(
self.partner1, message1)
dig2 = self.digest_model._get_or_create_by_partner(
self.partner1, message2)
dig1 = self.digest_model._get_or_create_by_user(self.user1)
dig2 = self.digest_model._get_or_create_by_user(self.user1)
self.assertEqual(dig1, dig2)
def test_create_or_update_digest(self):
partners = self.partner_model
partners |= self.partner1
partners |= self.partner2
partners = self.env['res.partner']
partners |= self.user1.partner_id
partners |= self.user2.partner_id
message1 = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
@ -63,72 +62,75 @@ class DigestCase(TransactionCase):
'subtype_id': self.subtype2.id,
})
# partner 1
self.digest_model.create_or_update(self.partner1, message1)
self.digest_model.create_or_update(self.partner1, message2)
p1dig = self.digest_model._get_or_create_by_partner(self.partner1)
self.digest_model.create_or_update(self.user1.partner_id, message1)
self.digest_model.create_or_update(self.user1.partner_id, message2)
p1dig = self.digest_model._get_or_create_by_user(self.user1)
self.assertIn(message1, p1dig.message_ids)
self.assertIn(message2, p1dig.message_ids)
# partner 2
self.digest_model.create_or_update(self.partner2, message1)
self.digest_model.create_or_update(self.partner2, message2)
p2dig = self.digest_model._get_or_create_by_partner(self.partner2)
self.digest_model.create_or_update(self.user2.partner_id, message1)
self.digest_model.create_or_update(self.user2.partner_id, message2)
p2dig = self.digest_model._get_or_create_by_user(self.user2)
self.assertIn(message1, p2dig.message_ids)
self.assertIn(message2, p2dig.message_ids)
def test_notify_partner_digest(self):
def test_notify_user_digest(self):
message = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
})
self.partner1.notify_email = 'digest'
self.user1.digest_mode = True
# notify partner
self.partner1._notify(message)
self.user1.partner_id._notify(message)
# we should find the message in its digest
dig1 = self.digest_model._get_or_create_by_partner(
self.partner1, message)
dig1 = self.digest_model._get_or_create_by_user(self.user1)
self.assertIn(message, dig1.message_ids)
def test_notify_partner_digest_followers(self):
self.partner3.message_subscribe(self.partner2.ids)
self.partner1.notify_email = 'digest'
self.partner2.notify_email = 'digest'
partners = self.partner1 + self.partner2
# subscribe a partner to the other one
self.user3.partner_id.message_subscribe(
partner_ids=self.user2.partner_id.ids)
self.user1.digest_mode = True
self.user2.digest_mode = True
partners = self.user1.partner_id + self.user2.partner_id
message = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
'res_id': self.partner3.id,
'res_id': self.user3.partner_id.id,
'model': 'res.partner',
'partner_ids': [(4, self.partner1.id)]
'partner_ids': [(4, self.user1.partner_id.id)]
})
# notify partners
partners._notify(message)
partners.with_context(foo=1)._notify(message)
# we should find the a digest for each partner
dig1 = self.digest_model._get_by_partner(self.partner1)
dig2 = self.digest_model._get_by_partner(self.partner2)
dig1 = self.digest_model._get_by_user(self.user1)
dig2 = self.digest_model._get_by_user(self.user2)
# and the message in them
self.assertIn(message, dig1.message_ids)
self.assertIn(message, dig2.message_ids)
# now we exclude followers
dig1.unlink()
dig2.unlink()
partners.with_context(notify_only_recipients=1)._notify(message)
partners.with_context(notify_only_recipients=True)._notify(message)
# we should find the a digest for each partner
self.assertTrue(self.digest_model._get_by_partner(self.partner1))
self.assertFalse(self.digest_model._get_by_partner(self.partner2))
self.assertTrue(self.digest_model._get_by_user(self.user1))
self.assertFalse(self.digest_model._get_by_user(self.user2))
def test_global_conf(self):
for k in ('email', 'comment', 'notification'):
self.assertIn(k, self.partner1._digest_enabled_message_types())
self.assertIn(
k, self.env['res.partner']._digest_enabled_message_types())
self.env['ir.config_parameter'].set_param(
'mail_digest.enabled_message_types',
'email,notification'
)
for k in ('email', 'notification'):
self.assertIn(k, self.partner1._digest_enabled_message_types())
self.assertIn(
k, self.env['res.partner']._digest_enabled_message_types())
self.assertNotIn(
'comment', self.partner1._digest_enabled_message_types())
'comment', self.env['res.partner']._digest_enabled_message_types())
def test_notify_partner_digest_global_disabled(self):
def test_notify_user_digest_global_disabled(self):
# change global conf
self.env['ir.config_parameter'].set_param(
'mail_digest.enabled_message_types',
@ -140,46 +142,46 @@ class DigestCase(TransactionCase):
# globally disabled type
'message_type': 'notification',
})
self.partner1.notify_email = 'digest'
self.user1.digest_mode = True
# notify partner
self.partner1._notify(message)
self.user1.partner_id._notify(message)
# we should not find any digest
self.assertFalse(self.digest_model._get_by_partner(self.partner1))
self.assertFalse(self.digest_model._get_by_user(self.user1))
def _create_for_partner(self, partner):
messages = {}
for type_id in (self.subtype1.id, self.subtype2.id):
for k in xrange(1, 3):
for k in range(1, 3):
key = '{}_{}'.format(type_id, k)
messages[key] = self.message_model.create({
'subject': 'My Subject {}'.format(key),
'body': 'My Body {}'.format(key),
'subtype_id': type_id,
})
self.digest_model.create_or_update(
partner, messages[key])
return self.digest_model._get_or_create_by_partner(partner)
self.digest_model.create_or_update(partner, messages[key])
return self.digest_model._get_by_user(partner.real_user_id)
def test_digest_group_messages(self):
dig = self._create_for_partner(self.partner1)
dig = self._create_for_partner(self.user1.partner_id)
grouped = dig._message_group_by()
for type_id in (self.subtype1.id, self.subtype2.id):
self.assertIn(type_id, grouped)
self.assertEqual(len(grouped[type_id]), 2)
def test_digest_mail_values(self):
dig = self._create_for_partner(self.partner1)
dig = self._create_for_partner(self.user1.partner_id)
values = dig._get_email_values()
expected = ('recipient_ids', 'subject', 'body_html')
for k in expected:
self.assertIn(k, values)
self.assertEqual(self.env.user.company_id.email, values['email_from'])
self.assertEqual([(4, self.partner1.id)], values['recipient_ids'])
self.assertEqual(
[(4, self.user1.partner_id.id)], values['recipient_ids'])
def test_digest_template(self):
default = self.env.ref('mail_digest.default_digest_tmpl')
dig = self._create_for_partner(self.partner1)
dig = self._create_for_partner(self.user1.partner_id)
# check default
self.assertEqual(dig.digest_template_id, default)
self.assertTrue(dig._get_email_values())

156
mail_digest/tests/test_partner_domains.py

@ -1,38 +1,44 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.tests.common import TransactionCase
class PartnerDomainCase(TransactionCase):
def setUp(self):
super(PartnerDomainCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2',
'email': 'partner2@test.foo.com',
})
self.partner3 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 3',
'email': 'partner3@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
def _assert_found(self, domain, not_found=False, partner=None):
partner = partner or self.partner1
from odoo.tests.common import SavepointCase
class PartnerDomainCase(SavepointCase):
@classmethod
def setUpClass(cls):
super(PartnerDomainCase, cls).setUpClass()
cls.partner_model = cls.env['res.partner']
cls.message_model = cls.env['mail.message']
cls.subtype_model = cls.env['mail.message.subtype']
user_model = cls.env['res.users'].with_context(
no_reset_password=True, tracking_disable=True)
cls.user1 = user_model.create({
'name': 'User 1',
'login': 'testuser1',
'email': 'testuser1@email.com',
})
cls.user2 = user_model.create({
'name': 'User 2',
'login': 'testuser2',
'email': 'testuser2@email.com',
})
cls.user3 = user_model.create({
'name': 'User 3',
'login': 'testuser3',
'email': 'testuser3@email.com',
})
cls.partner1 = cls.user1.partner_id
cls.partner2 = cls.user2.partner_id
cls.partner3 = cls.user3.partner_id
cls.subtype1 = cls.subtype_model.create({'name': 'Type 1'})
cls.subtype2 = cls.subtype_model.create({'name': 'Type 2'})
def _assert_found(self, partner, domain, not_found=False):
if not_found:
self.assertNotIn(partner, self.partner_model.search(domain))
else:
@ -43,17 +49,17 @@ class PartnerDomainCase(TransactionCase):
# because we call `_get_notify_by_email_domain` directly
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'always'
partner.real_user_id.notification_type = 'email'
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
self._assert_found(partner, domain)
domain = partner._get_notify_by_email_domain(message, digest=True)
self._assert_found(partner, domain, not_found=True)
def test_notify_domains_only_recipients(self):
# we don't set recipients
# because we call `_get_notify_by_email_domain` directly
self.partner1.notify_email = 'always'
self.partner2.notify_email = 'always'
self.partner1.real_user_id.notification_type = 'email'
self.partner2.real_user_id.notification_type = 'email'
partners = self.partner1 + self.partner2
# followers
self.partner3.message_subscribe(self.partner2.ids)
@ -66,42 +72,47 @@ class PartnerDomainCase(TransactionCase):
})
domain = partners._get_notify_by_email_domain(message)
# we find both of them since partner2 is a follower
self._assert_found(domain)
self._assert_found(domain, partner=self.partner2)
self._assert_found(self.partner1, domain)
self._assert_found(self.partner2, domain)
# no one here in digest mode
domain = partners._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
self._assert_found(domain, not_found=1, partner=self.partner2)
domain = partners._get_notify_by_email_domain(message, digest=True)
self._assert_found(self.partner1, domain, not_found=True)
self._assert_found(self.partner2, domain, not_found=True)
# include only recipients
domain = partners.with_context(
notify_only_recipients=1)._get_notify_by_email_domain(message)
self._assert_found(domain)
self._assert_found(domain, partner=self.partner2, not_found=1)
self._assert_found(self.partner1, domain)
self._assert_found(self.partner2, domain, not_found=True)
def test_notify_domains_digest(self):
# we don't set recipients
# because we call `_get_notify_by_email_domain` directly
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'digest'
partner.real_user_id.write({
'notification_type': 'email',
'digest_mode': True,
})
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain, not_found=1)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain)
self._assert_found(partner, domain, not_found=True)
domain = partner._get_notify_by_email_domain(message, digest=True)
self._assert_found(partner, domain)
def test_notify_domains_none(self):
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'none'
partner.real_user_id.write({
'notification_type': 'inbox',
})
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain, not_found=1)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
self._assert_found(partner, domain, not_found=True)
domain = partner._get_notify_by_email_domain(message, digest=True)
self._assert_found(partner, domain, not_found=True)
def test_notify_domains_match_type_digest(self):
# Test message subtype matches partner settings.
# The partner can have several `partner.notification.conf` records.
# The partner can have several `user.notification.conf` records.
# Each records establish notification rules by type.
# If you don't have any record in it, you allow all subtypes.
# Record `typeX` with `enable=True` enables notification for `typeX`.
@ -109,7 +120,10 @@ class PartnerDomainCase(TransactionCase):
partner = self.partner1
# enable digest
partner.notify_email = 'digest'
partner.real_user_id.write({
'notification_type': 'email',
'digest_mode': True,
})
message_t1 = self.message_model.create({
'body': 'My Body',
'subtype_id': self.subtype1.id,
@ -119,25 +133,25 @@ class PartnerDomainCase(TransactionCase):
'subtype_id': self.subtype2.id,
})
# enable subtype on partner
partner._notify_enable_subtype(self.subtype1)
partner.real_user_id._notify_enable_subtype(self.subtype1)
domain = partner._get_notify_by_email_domain(
message_t1, digest=True)
# notification enabled: we find the partner.
self._assert_found(domain)
self._assert_found(partner, domain)
# for subtype2 we don't have any explicit rule: we find the partner
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain)
self._assert_found(partner, domain)
# enable subtype2: find the partner anyway
partner._notify_enable_subtype(self.subtype2)
partner.real_user_id._notify_enable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain)
self._assert_found(partner, domain)
# disable subtype2: we don't find the partner anymore
partner._notify_disable_subtype(self.subtype2)
partner.real_user_id._notify_disable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain, not_found=1)
self._assert_found(partner, domain, not_found=True)
def test_notify_domains_match_type_always(self):
message_t1 = self.message_model.create({
@ -150,20 +164,20 @@ class PartnerDomainCase(TransactionCase):
})
# enable always
partner = self.partner1
partner.notify_email = 'always'
partner.real_user_id.notification_type = 'email'
# enable subtype on partner
partner._notify_enable_subtype(self.subtype1)
partner.real_user_id._notify_enable_subtype(self.subtype1)
domain = partner._get_notify_by_email_domain(message_t1)
# notification enabled: we find the partner.
self._assert_found(domain)
self._assert_found(partner, domain)
# for subtype2 we don't have any explicit rule: we find the partner
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain)
self._assert_found(partner, domain)
# enable subtype2: find the partner anyway
partner._notify_enable_subtype(self.subtype2)
partner.real_user_id._notify_enable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain)
self._assert_found(partner, domain)
# disable subtype2: we don't find the partner anymore
partner._notify_disable_subtype(self.subtype2)
partner.real_user_id._notify_disable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain, not_found=1)
self._assert_found(partner, domain, not_found=True)

119
mail_digest/tests/test_subtypes_conf.py

@ -1,136 +1,109 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# Copyright 2017-2018 Camptocamp - Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.tests.common import TransactionCase
from odoo.tests.common import SavepointCase
class SubtypesCase(TransactionCase):
class SubtypesCase(SavepointCase):
def setUp(self):
super(SubtypesCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
@classmethod
def setUpClass(cls):
super(SubtypesCase, cls).setUpClass()
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1!',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2!',
'email': 'partner2@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
user_model = cls.env['res.users'].with_context(
no_reset_password=True, tracking_disable=True)
cls.user1 = user_model.create({
'name': 'User 1',
'login': 'testuser1',
'email': 'testuser1@email.com',
})
cls.user2 = user_model.create({
'name': 'User 2',
'login': 'testuser2',
'email': 'testuser2@email.com',
})
subtype_model = cls.env['mail.message.subtype']
cls.subtype1 = subtype_model.create({'name': 'Type 1'})
cls.subtype2 = subtype_model.create({'name': 'Type 2'})
cls.subtype3 = subtype_model.create({'name': 'Type 3'})
cls.subtype4 = subtype_model.create({'name': 'Type 4'})
def _test_subtypes_rel(self):
# setup:
# t1, t2 enabled
# t3 disabled
# t4 no conf
self.subtype3 = self.subtype_model.create({'name': 'Type 3'})
self.subtype4 = self.subtype_model.create({'name': 'Type 4'})
# enable t1 t2
self.partner1._notify_enable_subtype(self.subtype1)
self.partner1._notify_enable_subtype(self.subtype2)
self.user1._notify_enable_subtype(self.subtype1)
self.user1._notify_enable_subtype(self.subtype2)
# disable t3
self.partner1._notify_disable_subtype(self.subtype3)
self.user1._notify_disable_subtype(self.subtype3)
def test_partner_computed_subtype(self):
def test_user_computed_subtype(self):
self._test_subtypes_rel()
# check computed fields
self.assertIn(
self.subtype1, self.partner1.enabled_notify_subtype_ids)
self.subtype1, self.user1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype1, self.partner1.disabled_notify_subtype_ids)
self.subtype1, self.user1.disabled_notify_subtype_ids)
self.assertIn(
self.subtype2, self.partner1.enabled_notify_subtype_ids)
self.subtype2, self.user1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype2, self.partner1.disabled_notify_subtype_ids)
self.subtype2, self.user1.disabled_notify_subtype_ids)
self.assertIn(
self.subtype3, self.partner1.disabled_notify_subtype_ids)
self.subtype3, self.user1.disabled_notify_subtype_ids)
self.assertNotIn(
self.subtype3, self.partner1.enabled_notify_subtype_ids)
self.subtype3, self.user1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype4,
self.partner1.enabled_notify_subtype_ids)
self.user1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype4,
self.partner1.disabled_notify_subtype_ids)
self.user1.disabled_notify_subtype_ids)
def test_partner_find_by_subtype_incl(self):
def test_find_user_by_subtype_incl(self):
self._test_subtypes_rel()
domain = [(
'enabled_notify_subtype_ids',
'in', (self.subtype1.id, self.subtype2.id),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'disabled_notify_subtype_ids', 'in', self.subtype3.id,
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'enabled_notify_subtype_ids', 'in', (self.subtype3.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertNotIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'enabled_notify_subtype_ids', 'in', (self.subtype4.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertNotIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'disabled_notify_subtype_ids', 'in', (self.subtype4.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertNotIn(self.user1, self.env['res.users'].search(domain))
def test_partner_find_by_subtype_escl(self):
def test_find_user_by_subtype_escl(self):
self._test_subtypes_rel()
domain = [(
'enabled_notify_subtype_ids',
'not in', (self.subtype4.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'disabled_notify_subtype_ids',
'not in', (self.subtype4.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'enabled_notify_subtype_ids',
'not in', (self.subtype3.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))
domain = [(
'disabled_notify_subtype_ids',
'not in', (self.subtype1.id, self.subtype2.id),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
self.assertIn(self.user1, self.env['res.users'].search(domain))

4
mail_digest/views/mail_digest_views.xml

@ -6,7 +6,7 @@
<field name="model">mail.digest</field>
<field name="arch" type="xml">
<tree string="Mail digest">
<field name="partner_id" />
<field name="user_id" />
<field name="mail_id" />
<field name="state" />
</tree>
@ -27,7 +27,7 @@
</div>
<group name="main" col="2">
<field name="name" />
<field name="partner_id" />
<field name="user_id" />
</group>
<group name="settings" col="2">
<field name="frequency" />

38
mail_digest/views/partner_views.xml

@ -1,38 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record id="notifications_emails_partner_info_form" model="ir.ui.view">
<field name="name">mail.notifications res.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="mail.view_emails_partner_info_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notify_email']" position="after">
<field name="notify_conf_ids" attrs="{'invisible': [('notify_email','=', 'none')]}"/>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="notification_form">
<field name="name">partner.notification.conf form</field>
<field name="model">partner.notification.conf</field>
<field name="arch" type="xml">
<form string="Notification">
<group name="main">
<field name="enabled" />
<field name="subtype_id" options="{'no_create': True}" />
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="notification_tree">
<field name="name">partner.notification.conf tree</field>
<field name="model">partner.notification.conf</field>
<field name="arch" type="xml">
<tree string="Notifications" editable="top">
<field name="enabled" />
<field name="subtype_id" />
</tree>
</field>
</record>
</odoo>

28
mail_digest/views/user_notification_views.xml

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="notification_form">
<field name="name">user.notification.conf form</field>
<field name="model">user.notification.conf</field>
<field name="arch" type="xml">
<form string="Notification">
<group name="main">
<field name="enabled" />
<field name="subtype_id" options="{'no_create': True}" />
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="notification_tree">
<field name="name">user.notification.conf tree</field>
<field name="model">user.notification.conf</field>
<field name="arch" type="xml">
<tree string="Notifications" editable="top">
<field name="enabled" />
<field name="subtype_id" />
</tree>
</field>
</record>
</odoo>

23
mail_digest/views/user_views.xml

@ -5,13 +5,14 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="mail.view_users_form_mail"/>
<field name="arch" type="xml">
<field name="notify_email" position="replace">
<field name="notification_type" position="replace">
<group name="notif_left">
<field name="notify_email" widget="radio" readonly="0"/>
<field name="notify_frequency" readonly="0"
attrs="{'invisible': [('notify_email', '!=', 'digest')]}" />
<field name="notification_type" widget="radio" readonly="0"/>
<field name="digest_mode" />
<field name="digest_frequency" readonly="0"
attrs="{'invisible': [('digest_mode', '=', False)]}" />
</group>
<group name="notif_right" attrs="{'invisible': [('notify_email','=', 'none')]}">
<group name="notif_right" attrs="{'invisible': [('notification_type','=', 'inbox')]}">
<label string="Enable/disable notifications by type" colspan="4" />
<field name="notify_conf_ids" nolabel="1" colspan="4" readonly="0" />
</group>
@ -25,17 +26,19 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="mail.view_users_form_simple_modif_mail"/>
<field name="arch" type="xml">
<field name="notify_email" position="replace">
<field name="notification_type" position="replace">
<group name="notif_left">
<field name="notify_email" widget="radio" readonly="0"/>
<field name="notify_frequency" readonly="0"
attrs="{'invisible': [('notify_email', '!=', 'digest')]}" />
<field name="digest_mode" />
<field name="notification_type" widget="radio" readonly="0"/>
<field name="digest_frequency" readonly="0"
attrs="{'invisible': [('digest_mode', '=', False)]}" />
</group>
<group name="notif_right" attrs="{'invisible': [('notify_email','=', 'none')]}">
<group name="notif_right" attrs="{'invisible': [('notification_type','=', 'inbox')]}">
<label string="Enable/disable notifications by type" colspan="4" />
<field name="notify_conf_ids" nolabel="1" colspan="4" readonly="0" />
</group>
</field>
</field>
</record>
</odoo>
Loading…
Cancel
Save