Simone Orsi
7 years ago
committed by
Holger Brunn
21 changed files with 489 additions and 563 deletions
-
15mail_digest/README.rst
-
9mail_digest/__manifest__.py
-
5mail_digest/data/config_param.xml
-
16mail_digest/data/ir_cron.xml
-
39mail_digest/demo/ir_ui_view.xml
-
10mail_digest/demo/mail_template.xml
-
1mail_digest/models/__init__.py
-
64mail_digest/models/mail_digest.py
-
169mail_digest/models/res_partner.py
-
113mail_digest/models/res_users.py
-
33mail_digest/models/user_notification_conf.py
-
4mail_digest/security/ir.model.access.csv
-
8mail_digest/security/record_rules.xml
-
2mail_digest/templates/digest_default.xml
-
146mail_digest/tests/test_digest.py
-
140mail_digest/tests/test_partner_domains.py
-
115mail_digest/tests/test_subtypes_conf.py
-
4mail_digest/views/mail_digest_views.xml
-
38mail_digest/views/partner_views.xml
-
28mail_digest/views/user_notification_views.xml
-
23mail_digest/views/user_views.xml
@ -1,9 +1,8 @@ |
|||||
<odoo> |
|
||||
<data noupdate="1"> |
|
||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo noupdate="1"> |
||||
<record id="mail_digest_enabled_message_types" model="ir.config_parameter"> |
<record id="mail_digest_enabled_message_types" model="ir.config_parameter"> |
||||
<field name="key">mail_digest.enabled_message_types</field> |
<field name="key">mail_digest.enabled_message_types</field> |
||||
<!-- enabled by default for each message type --> |
<!-- enabled by default for each message type --> |
||||
<field name="value">email,notification,comment</field> |
<field name="value">email,notification,comment</field> |
||||
</record> |
</record> |
||||
</data> |
|
||||
</odoo> |
</odoo> |
@ -1,27 +1,23 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<odoo> |
|
||||
<data noupdate="1"> |
|
||||
|
<odoo noupdate="1"> |
||||
<record forcecreate="True" id="ir_cron_mail_digest_daily_action" model="ir.cron"> |
<record forcecreate="True" id="ir_cron_mail_digest_daily_action" model="ir.cron"> |
||||
<field name="name">Digest mail process - daily</field> |
<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="user_id" ref="base.user_root"/> |
||||
<field name="interval_number">1</field> |
<field name="interval_number">1</field> |
||||
<field name="interval_type">days</field> |
<field name="interval_type">days</field> |
||||
<field name="numbercall">-1</field> |
<field name="numbercall">-1</field> |
||||
<field eval="False" name="doall"/> |
<field eval="False" name="doall"/> |
||||
<field eval="'mail.digest'" name="model"/> |
|
||||
<field eval="'process'" name="function"/> |
|
||||
<field eval="'()'" name="args"/> |
|
||||
|
<field name="code">model.process()</field> |
||||
</record> |
</record> |
||||
<record forcecreate="True" id="ir_cron_mail_digest_weekly_action" model="ir.cron"> |
<record forcecreate="True" id="ir_cron_mail_digest_weekly_action" model="ir.cron"> |
||||
<field name="name">Digest mail process - weekly</field> |
<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="user_id" ref="base.user_root"/> |
||||
<field name="interval_number">1</field> |
<field name="interval_number">1</field> |
||||
<field name="interval_type">weeks</field> |
<field name="interval_type">weeks</field> |
||||
<field name="numbercall">-1</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"/> |
|
||||
|
<field name="doall" eval="False"/> |
||||
|
<field name="code">model.process(frequency='weekly')</field> |
||||
</record> |
</record> |
||||
</data> |
|
||||
</odoo> |
</odoo> |
@ -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> |
|
@ -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,3 +1,4 @@ |
|||||
from . import mail_digest |
from . import mail_digest |
||||
|
from . import user_notification_conf |
||||
from . import res_partner |
from . import res_partner |
||||
from . import res_users |
from . import res_users |
@ -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) |
@ -1,4 +1,4 @@ |
|||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
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_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 |
@ -1,14 +1,14 @@ |
|||||
<?xml version="1.0"?> |
<?xml version="1.0"?> |
||||
<odoo> |
<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_read" eval="False"/> |
||||
<field name="perm_create" eval="False"/> |
<field name="perm_create" eval="False"/> |
||||
<field name="perm_write" eval="True"/> |
<field name="perm_write" eval="True"/> |
||||
<field name="perm_unlink" 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> |
</record> |
||||
|
|
||||
</odoo> |
</odoo> |
@ -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). |
# 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', |
|
||||
|
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', |
||||
}) |
}) |
||||
self.partner2 = self.partner_model.with_context( |
|
||||
tracking_disable=1).create({ |
|
||||
'name': 'Partner 2!', |
|
||||
'email': 'partner2@test.foo.com', |
|
||||
|
cls.user2 = user_model.create({ |
||||
|
'name': 'User 2', |
||||
|
'login': 'testuser2', |
||||
|
'email': 'testuser2@email.com', |
||||
}) |
}) |
||||
self.subtype1 = self.subtype_model.create({'name': 'Type 1'}) |
|
||||
self.subtype2 = self.subtype_model.create({'name': 'Type 2'}) |
|
||||
|
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): |
def _test_subtypes_rel(self): |
||||
# setup: |
# setup: |
||||
# t1, t2 enabled |
# t1, t2 enabled |
||||
# t3 disabled |
# t3 disabled |
||||
# t4 no conf |
# t4 no conf |
||||
self.subtype3 = self.subtype_model.create({'name': 'Type 3'}) |
|
||||
self.subtype4 = self.subtype_model.create({'name': 'Type 4'}) |
|
||||
# enable t1 t2 |
# 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 |
# 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() |
self._test_subtypes_rel() |
||||
# check computed fields |
# check computed fields |
||||
self.assertIn( |
self.assertIn( |
||||
self.subtype1, self.partner1.enabled_notify_subtype_ids) |
|
||||
|
self.subtype1, self.user1.enabled_notify_subtype_ids) |
||||
self.assertNotIn( |
self.assertNotIn( |
||||
self.subtype1, self.partner1.disabled_notify_subtype_ids) |
|
||||
|
self.subtype1, self.user1.disabled_notify_subtype_ids) |
||||
self.assertIn( |
self.assertIn( |
||||
self.subtype2, self.partner1.enabled_notify_subtype_ids) |
|
||||
|
self.subtype2, self.user1.enabled_notify_subtype_ids) |
||||
self.assertNotIn( |
self.assertNotIn( |
||||
self.subtype2, self.partner1.disabled_notify_subtype_ids) |
|
||||
|
self.subtype2, self.user1.disabled_notify_subtype_ids) |
||||
self.assertIn( |
self.assertIn( |
||||
self.subtype3, self.partner1.disabled_notify_subtype_ids) |
|
||||
|
self.subtype3, self.user1.disabled_notify_subtype_ids) |
||||
self.assertNotIn( |
self.assertNotIn( |
||||
self.subtype3, self.partner1.enabled_notify_subtype_ids) |
|
||||
|
self.subtype3, self.user1.enabled_notify_subtype_ids) |
||||
self.assertNotIn( |
self.assertNotIn( |
||||
self.subtype4, |
self.subtype4, |
||||
self.partner1.enabled_notify_subtype_ids) |
|
||||
|
self.user1.enabled_notify_subtype_ids) |
||||
self.assertNotIn( |
self.assertNotIn( |
||||
self.subtype4, |
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() |
self._test_subtypes_rel() |
||||
domain = [( |
domain = [( |
||||
'enabled_notify_subtype_ids', |
'enabled_notify_subtype_ids', |
||||
'in', (self.subtype1.id, self.subtype2.id), |
'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 = [( |
domain = [( |
||||
'disabled_notify_subtype_ids', 'in', self.subtype3.id, |
'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 = [( |
domain = [( |
||||
'enabled_notify_subtype_ids', 'in', (self.subtype3.id, ), |
'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 = [( |
domain = [( |
||||
'enabled_notify_subtype_ids', 'in', (self.subtype4.id, ), |
'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 = [( |
domain = [( |
||||
'disabled_notify_subtype_ids', 'in', (self.subtype4.id, ), |
'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() |
self._test_subtypes_rel() |
||||
domain = [( |
domain = [( |
||||
'enabled_notify_subtype_ids', |
'enabled_notify_subtype_ids', |
||||
'not in', (self.subtype4.id, ), |
'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 = [( |
domain = [( |
||||
'disabled_notify_subtype_ids', |
'disabled_notify_subtype_ids', |
||||
'not in', (self.subtype4.id, ), |
'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 = [( |
domain = [( |
||||
'enabled_notify_subtype_ids', |
'enabled_notify_subtype_ids', |
||||
'not in', (self.subtype3.id, ), |
'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 = [( |
domain = [( |
||||
'disabled_notify_subtype_ids', |
'disabled_notify_subtype_ids', |
||||
'not in', (self.subtype1.id, self.subtype2.id), |
'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)) |
@ -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> |
|
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue