Browse Source
Merge pull request #158 from camptocamp/add-mail_digest
Merge pull request #158 from camptocamp/add-mail_digest
[add] mail_digestpull/184/head
Pedro M. Baeza
8 years ago
committed by
GitHub
22 changed files with 1557 additions and 0 deletions
-
90mail_digest/README.rst
-
1mail_digest/__init__.py
-
22mail_digest/__openerp__.py
-
27mail_digest/data/ir_cron.xml
-
39mail_digest/demo/ir_ui_view.xml
-
10mail_digest/demo/mail_template.xml
-
184mail_digest/i18n/de.po
-
175mail_digest/i18n/mail_digest.pot
-
BINmail_digest/images/preview.png
-
2mail_digest/models/__init__.py
-
183mail_digest/models/mail_digest.py
-
208mail_digest/models/res_partner.py
-
5mail_digest/security/ir.model.access.csv
-
BINmail_digest/static/description/icon.png
-
47mail_digest/templates/digest_default.xml
-
3mail_digest/tests/__init__.py
-
147mail_digest/tests/test_digest.py
-
169mail_digest/tests/test_partner_domains.py
-
136mail_digest/tests/test_subtypes_conf.py
-
50mail_digest/views/mail_digest_views.xml
-
41mail_digest/views/partner_views.xml
-
18mail_digest/views/user_views.xml
@ -0,0 +1,90 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
========================= |
|||
Mail digest notifications |
|||
========================= |
|||
|
|||
Features |
|||
-------- |
|||
|
|||
This module allows users/partners to: |
|||
|
|||
* select "digest" mode in their notification settings |
|||
* with digest mode on select a frequency: "daily" or "weekly" |
|||
* configure specific rules per message subtype (enabled/disabled) |
|||
|
|||
to receive or to not receive any email notification for a given subtype. |
|||
|
|||
The preference tab on user's form will look like: |
|||
|
|||
.. image:: ./images/preview.png |
|||
|
|||
|
|||
Behavior |
|||
-------- |
|||
|
|||
When a partner with digest mode on is notified with a message of type email or an email |
|||
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: |
|||
|
|||
* no record for type: message passes |
|||
* record disabled for type: message don't pass |
|||
* record enabled for type: message pass |
|||
|
|||
NOTE: under the hood the digest notification logic excludes followers to be notified, |
|||
since you really want to notify only mail.digest's partner. |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* take full control of message and email template. |
|||
|
|||
Right now the notification message and the digest mail itself is wrapped inside Odoo mail template. |
|||
We should be able to customize this easily. |
|||
|
|||
|
|||
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. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Simone Orsi <simone.orsi@camptocamp.com> |
|||
|
|||
|
|||
Funders |
|||
------- |
|||
|
|||
The development of this module has been financially supported by: `Fluxdock.io <https://fluxdock.io>`_ |
|||
|
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1 @@ |
|||
from . import models |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
{ |
|||
'name': 'Mail digest', |
|||
'summary': """Basic digest mail handling.""", |
|||
'version': '9.0.1.0.0', |
|||
'license': 'AGPL-3', |
|||
'author': 'Camptocamp,Odoo Community Association (OCA)', |
|||
'depends': [ |
|||
'mail', |
|||
], |
|||
'data': [ |
|||
'data/ir_cron.xml', |
|||
'security/ir.model.access.csv', |
|||
'views/mail_digest_views.xml', |
|||
'views/partner_views.xml', |
|||
'views/user_views.xml', |
|||
'templates/digest_default.xml', |
|||
], |
|||
} |
@ -0,0 +1,27 @@ |
|||
<?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> |
@ -0,0 +1,39 @@ |
|||
<?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> |
@ -0,0 +1,10 @@ |
|||
<?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> |
@ -0,0 +1,184 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_digest |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 9.0c\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2017-05-23 07:08+0000\n" |
|||
"PO-Revision-Date: 2017-04-27 16:55+0200\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"Language: de\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: \n" |
|||
"X-Generator: Poedit 1.8.9\n" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_uid |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_uid |
|||
msgid "Created by" |
|||
msgstr "Angelegt von" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_date |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_date |
|||
msgid "Created on" |
|||
msgstr "Angelegt am" |
|||
|
|||
#. module: mail_digest |
|||
#: selection:res.partner,notify_frequency:0 |
|||
msgid "Daily" |
|||
msgstr "Täglich" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/mail_digest.py:106 |
|||
msgid "Daily update" |
|||
msgstr "täglich Übersicht" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/res_partner.py:11 |
|||
#: model:ir.actions.act_window,name:mail_digest.action_digest_all |
|||
#: model:ir.ui.menu,name:mail_digest.menu_email_digest |
|||
#, python-format |
|||
msgid "Digest" |
|||
msgstr "Übersicht" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_display_name |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_display_name |
|||
msgid "Display Name" |
|||
msgstr "Angezeigter Name" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_enabled |
|||
msgid "Enabled" |
|||
msgstr "Aktivieren" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_frequency |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_frequency |
|||
msgid "Frequency" |
|||
msgstr "Häufigkeit" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.default_digest_tmpl |
|||
msgid "Hello," |
|||
msgstr "Guten Tag" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_id |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest___last_update |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "Zuletzt geändert am" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_uid |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "Zuletzt aktualisiert durch" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_date |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "Zuletzt aktualisiert am" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_mail_id |
|||
msgid "Mail" |
|||
msgstr "E-Mail" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_tree |
|||
msgid "Mail digest" |
|||
msgstr "Übersicht" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_message_ids |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form |
|||
msgid "Messages" |
|||
msgstr "Mitteilungen" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_name |
|||
msgid "Name" |
|||
msgstr "Bezeichnung" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.notification_form |
|||
msgid "Notification" |
|||
msgstr "Benachrichtigung" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_subtype_id |
|||
msgid "Notification type" |
|||
msgstr "Benachrichtigung" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_conf_ids |
|||
#: model:ir.ui.view,arch_db:mail_digest.notification_tree |
|||
msgid "Notifications" |
|||
msgstr "Benachrichtigungen" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_res_partner |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_partner_id |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_partner_id |
|||
msgid "Partner" |
|||
msgstr "Partner" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_disabled_notify_subtype_ids |
|||
msgid "Partner disabled subtypes" |
|||
msgstr "Partner disabled subtypes" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_enabled_notify_subtype_ids |
|||
msgid "Partner enabled subtypes" |
|||
msgstr "Partner enabled subtypes" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_partner_notification_conf |
|||
msgid "Partner notification configuration" |
|||
msgstr "Partner notification configuration" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_state |
|||
msgid "Status" |
|||
msgstr "Status" |
|||
|
|||
#. module: mail_digest |
|||
#: selection:res.partner,notify_frequency:0 |
|||
msgid "Weekly" |
|||
msgstr "Wöchentlich" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/mail_digest.py:108 |
|||
msgid "Weekly update" |
|||
msgstr "wöchentlich Übersicht" |
|||
|
|||
#. module: mail_digest |
|||
#: sql_constraint:partner.notification.conf:0 |
|||
msgid "You can have only one configuration per subtype!" |
|||
msgstr "You can have only one configuration per subtype!" |
|||
|
|||
#~ msgid "Mail notification" |
|||
#~ msgstr "Mail notification" |
|||
|
|||
#~ msgid "Mail notifications control panel" |
|||
#~ msgstr "Mail notifications control panel" |
|||
|
|||
#~ msgid "Message subtypes" |
|||
#~ msgstr "Nachrichten-Subtyp" |
@ -0,0 +1,175 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_digest |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 9.0c\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2017-05-23 07:08+0000\n" |
|||
"PO-Revision-Date: 2017-05-23 07:08+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_uid |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_date |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: selection:res.partner,notify_frequency:0 |
|||
msgid "Daily" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/mail_digest.py:106 |
|||
#, python-format |
|||
msgid "Daily update" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/res_partner.py:11 |
|||
#: model:ir.actions.act_window,name:mail_digest.action_digest_all |
|||
#: model:ir.ui.menu,name:mail_digest.menu_email_digest |
|||
#, python-format |
|||
msgid "Digest" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_display_name |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_enabled |
|||
msgid "Enabled" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_frequency |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_frequency |
|||
msgid "Frequency" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.default_digest_tmpl |
|||
msgid "Hello," |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_id |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest___last_update |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_uid |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_date |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_mail_id |
|||
msgid "Mail" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_tree |
|||
msgid "Mail digest" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_message_ids |
|||
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form |
|||
msgid "Messages" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_name |
|||
msgid "Name" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.ui.view,arch_db:mail_digest.notification_form |
|||
msgid "Notification" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_subtype_id |
|||
msgid "Notification type" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_conf_ids |
|||
#: model:ir.ui.view,arch_db:mail_digest.notification_tree |
|||
msgid "Notifications" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_res_partner |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_partner_id |
|||
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_partner_id |
|||
msgid "Partner" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_disabled_notify_subtype_ids |
|||
msgid "Partner disabled subtypes" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_enabled_notify_subtype_ids |
|||
msgid "Partner enabled subtypes" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model,name:mail_digest.model_partner_notification_conf |
|||
msgid "Partner notification configuration" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_state |
|||
msgid "Status" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: selection:res.partner,notify_frequency:0 |
|||
msgid "Weekly" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: code:addons/mail_digest/models/mail_digest.py:108 |
|||
#, python-format |
|||
msgid "Weekly update" |
|||
msgstr "" |
|||
|
|||
#. module: mail_digest |
|||
#: sql_constraint:partner.notification.conf:0 |
|||
msgid "You can have only one configuration per subtype!" |
|||
msgstr "" |
After Width: 837 | Height: 253 | Size: 20 KiB |
@ -0,0 +1,2 @@ |
|||
from . import mail_digest |
|||
from . import res_partner |
@ -0,0 +1,183 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
from openerp import fields, models, api, _ |
|||
|
|||
import logging |
|||
|
|||
logger = logging.getLogger('[mail_digest]') |
|||
|
|||
|
|||
class MailDigest(models.Model): |
|||
_name = 'mail.digest' |
|||
_description = 'Mail digest' |
|||
_order = 'create_date desc' |
|||
|
|||
name = fields.Char( |
|||
string="Name", |
|||
compute="_compute_name", |
|||
readonly=True, |
|||
) |
|||
# maybe we can retrieve the from messages? |
|||
partner_id = fields.Many2one( |
|||
string='Partner', |
|||
comodel_name='res.partner', |
|||
readonly=True, |
|||
required=True, |
|||
ondelete='cascade', |
|||
) |
|||
frequency = fields.Selection( |
|||
related='partner_id.notify_frequency', |
|||
readonly=True, |
|||
) |
|||
message_ids = fields.Many2many( |
|||
comodel_name='mail.message', |
|||
string='Messages' |
|||
) |
|||
# TODO: take care of `auto_delete` feature |
|||
mail_id = fields.Many2one( |
|||
'mail.mail', |
|||
'Mail', |
|||
ondelete='set null', |
|||
) |
|||
state = fields.Selection(related='mail_id.state') |
|||
|
|||
@api.multi |
|||
@api.depends("partner_id", "partner_id.notify_frequency") |
|||
def _compute_name(self): |
|||
for rec in self: |
|||
rec.name = u'{} - {}'.format( |
|||
rec.partner_id.name, rec._get_subject()) |
|||
|
|||
@api.model |
|||
def create_or_update(self, partners, message, subtype_id=None): |
|||
subtype_id = subtype_id or message.subtype_id |
|||
for partner in partners: |
|||
digest = self._get_or_create_by_partner(partner, message) |
|||
digest.message_ids |= message |
|||
return True |
|||
|
|||
@api.model |
|||
def _get_by_partner(self, partner, mail_id=False): |
|||
domain = [ |
|||
('partner_id', '=', partner.id), |
|||
('mail_id', '=', mail_id), |
|||
] |
|||
return self.search(domain, limit=1) |
|||
|
|||
@api.model |
|||
def _get_or_create_by_partner(self, partner, message=None, mail_id=False): |
|||
existing = self._get_by_partner(partner, mail_id=mail_id) |
|||
if existing: |
|||
return existing |
|||
values = {'partner_id': partner.id, } |
|||
return self.create(values) |
|||
|
|||
@api.model |
|||
def _message_group_by_key(self, msg): |
|||
return msg.subtype_id.id |
|||
|
|||
@api.multi |
|||
def _message_group_by(self): |
|||
self.ensure_one() |
|||
grouped = {} |
|||
for msg in self.message_ids: |
|||
grouped.setdefault(self._message_group_by_key(msg), []).append(msg) |
|||
return grouped |
|||
|
|||
def _get_template(self): |
|||
# TODO: move this to a configurable field |
|||
return self.env.ref('mail_digest.default_digest_tmpl') |
|||
|
|||
def _get_site_name(self): |
|||
# default to company |
|||
name = self.env.user.company_id.name |
|||
if 'website' in self.env: |
|||
try: |
|||
ws = self.env['website'].get_current_website() |
|||
except RuntimeError: |
|||
# RuntimeError: object unbound -> no website request |
|||
ws = None |
|||
if ws: |
|||
name = ws.name |
|||
return name |
|||
|
|||
@api.multi |
|||
def _get_subject(self): |
|||
# TODO: shall we move this to computed field? |
|||
self.ensure_one() |
|||
subject = self._get_site_name() + ' ' |
|||
if self.partner_id.notify_frequency == 'daily': |
|||
subject += _('Daily update') |
|||
elif self.partner_id.notify_frequency == 'weekly': |
|||
subject += _('Weekly update') |
|||
return subject |
|||
|
|||
@api.multi |
|||
def _get_template_values(self): |
|||
self.ensure_one() |
|||
subject = self._get_subject() |
|||
template_values = { |
|||
'digest': self, |
|||
'subject': subject, |
|||
'grouped_messages': self._message_group_by(), |
|||
'base_url': |
|||
self.env['ir.config_parameter'].get_param('web.base.url'), |
|||
} |
|||
return template_values |
|||
|
|||
@api.multi |
|||
def _get_email_values(self, template=None): |
|||
self.ensure_one() |
|||
template = template or self._get_template() |
|||
subject = self._get_subject() |
|||
template_values = self._get_template_values() |
|||
values = { |
|||
'email_from': self.env.user.company_id.email, |
|||
'recipient_ids': [(4, self.partner_id.id)], |
|||
'subject': subject, |
|||
'body_html': template.with_context( |
|||
**self._template_context() |
|||
).render(template_values), |
|||
} |
|||
return values |
|||
|
|||
def _create_mail_context(self): |
|||
return { |
|||
'notify_only_recipients': True, |
|||
} |
|||
|
|||
@api.multi |
|||
def _template_context(self): |
|||
self.ensure_one() |
|||
return { |
|||
'lang': self.partner_id.lang, |
|||
} |
|||
|
|||
@api.multi |
|||
def create_email(self, template=None): |
|||
mail_model = self.env['mail.mail'].with_context( |
|||
**self._create_mail_context()) |
|||
created = [] |
|||
for item in self: |
|||
if not item.message_ids: |
|||
# useless to create a mail for a digest w/ messages |
|||
# messages could be deleted by admin for instance. |
|||
continue |
|||
values = item.with_context( |
|||
**item._template_context() |
|||
)._get_email_values(template=template) |
|||
item.mail_id = mail_model.create(values) |
|||
created.append(item.id) |
|||
if created: |
|||
logger.info('Create email for digest IDS=%s', str(created)) |
|||
|
|||
@api.model |
|||
def process(self, frequency='daily', domain=None): |
|||
if not domain: |
|||
domain = [ |
|||
('mail_id', '=', False), |
|||
('partner_id.notify_frequency', '=', frequency), |
|||
] |
|||
self.search(domain).create_email() |
@ -0,0 +1,208 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
from openerp import models, fields, 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): |
|||
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) |
|||
|
|||
@api.multi |
|||
def _notify(self, message, force_send=False, user_signature=True): |
|||
"""Override to delegate domain generation.""" |
|||
# notify_by_email |
|||
email_domain = self._get_notify_by_email_domain(message) |
|||
# `sudo` from original odoo method |
|||
# 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( |
|||
message, force_send=force_send, user_signature=user_signature) |
|||
# notify_by_digest |
|||
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 |
|||
|
|||
@api.multi |
|||
def _notify_by_digest(self, message): |
|||
message_sudo = message.sudo() |
|||
if not message_sudo.message_type == 'email': |
|||
return |
|||
self.env['mail.digest'].sudo().create_or_update(self, message) |
|||
|
|||
@api.model |
|||
def _get_notify_by_email_domain(self, message, digest=False): |
|||
"""Return domain to collect partners to be notified by email. |
|||
|
|||
:param message: instance of mail.message |
|||
:param digest: include/exclude digest enabled partners |
|||
|
|||
NOTE: since mail.mail inherits from mail.message |
|||
this method is called even when |
|||
we create the final email for mail.digest object. |
|||
Here we introduce a new context flag `notify_only_recipients` |
|||
to explicitely retrieve only partners among message's recipients. |
|||
""" |
|||
|
|||
message_sudo = message.sudo() |
|||
channels = message.channel_ids.filtered( |
|||
lambda channel: channel.email_send) |
|||
email = message_sudo.author_id \ |
|||
and message_sudo.author_id.email or message.email_from |
|||
|
|||
ids = self.ids |
|||
if self.env.context.get('notify_only_recipients'): |
|||
ids = [x for x in ids if x in message.partner_ids.ids] |
|||
domain = [ |
|||
'|', |
|||
('id', 'in', ids), |
|||
('channel_ids', 'in', channels.ids), |
|||
('email', '!=', 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 |
|||
|
|||
@api.model |
|||
def _get_domain_subtype_leaf(self, subtype): |
|||
return [ |
|||
'|', |
|||
('disabled_notify_subtype_ids', 'not in', (subtype.id, )), |
|||
('enabled_notify_subtype_ids', 'in', (subtype.id, )), |
|||
] |
|||
|
|||
@api.multi |
|||
def _notify_update_subtype(self, subtype, enable): |
|||
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): |
|||
self._notify_update_subtype(subtype, True) |
|||
|
|||
@api.multi |
|||
def _notify_disable_subtype(self, 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, |
|||
) |
|||
enabled = fields.Boolean(default=True, index=True) |
@ -0,0 +1,5 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_mail_digest_all,mail.digest.all,model_mail_digest,,1,0,0,0 |
|||
access_partner_notification_conf_all,partner.notification.all,model_partner_notification_conf,,1,0,0,0 |
|||
access_mail_digest_system,mail.digest.all,model_mail_digest,base.group_system,1,1,1,1 |
|||
access_partner_notification_conf_system,partner.notification.all,model_partner_notification_conf,base.group_system,1,1,1,1 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,47 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<template id="digest_layout" name="Mail digest layout"> |
|||
<html> |
|||
<body> |
|||
<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-raw="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> |
|||
</body> |
|||
</html> |
|||
</template> |
|||
|
|||
<template id="default_digest_tmpl" name="Mail digest default template"> |
|||
<t t-call="mail_digest.digest_layout"> |
|||
<div id="mail_content"> |
|||
<h2>Hello,</h2> |
|||
<div id="mail_inner_content"> |
|||
<t t-foreach="grouped_messages.iterkeys()" t-as="gkey"> |
|||
<t t-set="messages" t-value="grouped_messages[gkey]" /> |
|||
<t t-foreach="messages" t-as="msg"> |
|||
<div style="margin:20px"> |
|||
<h3 t-esc="msg.subject" /> |
|||
<t t-raw="msg.body" /> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
</template> |
|||
|
|||
</odoo> |
@ -0,0 +1,3 @@ |
|||
from . import test_digest |
|||
from . import test_partner_domains |
|||
from . import test_subtypes_conf |
@ -0,0 +1,147 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
from openerp.tests.common import TransactionCase |
|||
|
|||
|
|||
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'}) |
|||
|
|||
def test_get_or_create_digest(self): |
|||
message1 = self.message_model.create({ |
|||
'body': 'My Body 1', |
|||
'subtype_id': self.subtype1.id, |
|||
}) |
|||
message2 = 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) |
|||
self.assertEqual(dig1, dig2) |
|||
|
|||
def test_create_or_update_digest(self): |
|||
partners = self.partner_model |
|||
partners |= self.partner1 |
|||
partners |= self.partner2 |
|||
message1 = self.message_model.create({ |
|||
'body': 'My Body 1', |
|||
'subtype_id': self.subtype1.id, |
|||
}) |
|||
message2 = self.message_model.create({ |
|||
'body': 'My Body 2', |
|||
'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.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.assertIn(message1, p2dig.message_ids) |
|||
self.assertIn(message2, p2dig.message_ids) |
|||
|
|||
def test_notify_partner_digest(self): |
|||
message = self.message_model.create({ |
|||
'body': 'My Body 1', |
|||
'subtype_id': self.subtype1.id, |
|||
}) |
|||
self.partner1.notify_email = 'digest' |
|||
# notify partner |
|||
self.partner1._notify(message) |
|||
# we should find the message in its digest |
|||
dig1 = self.digest_model._get_or_create_by_partner( |
|||
self.partner1, message) |
|||
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 |
|||
message = self.message_model.create({ |
|||
'body': 'My Body 1', |
|||
'subtype_id': self.subtype1.id, |
|||
'res_id': self.partner3.id, |
|||
'model': 'res.partner', |
|||
'partner_ids': [(4, self.partner1.id)] |
|||
}) |
|||
# notify partners |
|||
partners._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) |
|||
# 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) |
|||
# 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)) |
|||
|
|||
def _create_for_partner(self, partner): |
|||
messages = {} |
|||
for type_id in (self.subtype1.id, self.subtype2.id): |
|||
for k in xrange(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) |
|||
|
|||
def test_digest_group_messages(self): |
|||
dig = self._create_for_partner(self.partner1) |
|||
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) |
|||
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']) |
@ -0,0 +1,169 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
from openerp.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 |
|||
if not_found: |
|||
self.assertNotIn(partner, self.partner_model.search(domain)) |
|||
else: |
|||
self.assertIn(partner, self.partner_model.search(domain)) |
|||
|
|||
def test_notify_domains_always(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 = 'always' |
|||
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) |
|||
|
|||
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' |
|||
partners = self.partner1 + self.partner2 |
|||
# followers |
|||
self.partner3.message_subscribe(self.partner2.ids) |
|||
# partner1 is the only recipient |
|||
message = self.message_model.create({ |
|||
'body': 'My Body', |
|||
'res_id': self.partner3.id, |
|||
'model': 'res.partner', |
|||
'partner_ids': [(4, self.partner1.id)] |
|||
}) |
|||
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) |
|||
# 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) |
|||
|
|||
# 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) |
|||
|
|||
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' |
|||
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) |
|||
|
|||
def test_notify_domains_none(self): |
|||
message = self.message_model.create({'body': 'My Body', }) |
|||
partner = self.partner1 |
|||
partner.notify_email = 'none' |
|||
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) |
|||
|
|||
def test_notify_domains_match_type_digest(self): |
|||
# Test message subtype matches partner settings. |
|||
# The partner can have several `partner.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`. |
|||
# Record `typeX` with `enable=False` disables notification for `typeX`. |
|||
|
|||
partner = self.partner1 |
|||
# enable digest |
|||
partner.notify_email = 'digest' |
|||
message_t1 = self.message_model.create({ |
|||
'body': 'My Body', |
|||
'subtype_id': self.subtype1.id, |
|||
}) |
|||
message_t2 = self.message_model.create({ |
|||
'body': 'My Body', |
|||
'subtype_id': self.subtype2.id, |
|||
}) |
|||
# enable subtype on partner |
|||
partner._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) |
|||
# 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) |
|||
# enable subtype2: find the partner anyway |
|||
partner._notify_enable_subtype(self.subtype2) |
|||
domain = partner._get_notify_by_email_domain( |
|||
message_t2, digest=True) |
|||
self._assert_found(domain) |
|||
# disable subtype2: we don't find the partner anymore |
|||
partner._notify_disable_subtype(self.subtype2) |
|||
domain = partner._get_notify_by_email_domain( |
|||
message_t2, digest=True) |
|||
self._assert_found(domain, not_found=1) |
|||
|
|||
def test_notify_domains_match_type_always(self): |
|||
message_t1 = self.message_model.create({ |
|||
'body': 'My Body', |
|||
'subtype_id': self.subtype1.id, |
|||
}) |
|||
message_t2 = self.message_model.create({ |
|||
'body': 'My Body', |
|||
'subtype_id': self.subtype2.id, |
|||
}) |
|||
# enable always |
|||
partner = self.partner1 |
|||
partner.notify_email = 'always' |
|||
# enable subtype on partner |
|||
partner._notify_enable_subtype(self.subtype1) |
|||
domain = partner._get_notify_by_email_domain(message_t1) |
|||
# notification enabled: we find the partner. |
|||
self._assert_found(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) |
|||
# enable subtype2: find the partner anyway |
|||
partner._notify_enable_subtype(self.subtype2) |
|||
domain = partner._get_notify_by_email_domain(message_t2) |
|||
self._assert_found(domain) |
|||
# disable subtype2: we don't find the partner anymore |
|||
partner._notify_disable_subtype(self.subtype2) |
|||
domain = partner._get_notify_by_email_domain(message_t2) |
|||
self._assert_found(domain, not_found=1) |
@ -0,0 +1,136 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com> |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
|||
|
|||
from openerp.tests.common import TransactionCase |
|||
|
|||
|
|||
class SubtypesCase(TransactionCase): |
|||
|
|||
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'] |
|||
|
|||
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'}) |
|||
|
|||
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) |
|||
# disable t3 |
|||
self.partner1._notify_disable_subtype(self.subtype3) |
|||
|
|||
def test_partner_computed_subtype(self): |
|||
self._test_subtypes_rel() |
|||
# check computed fields |
|||
self.assertIn( |
|||
self.subtype1, self.partner1.enabled_notify_subtype_ids) |
|||
self.assertNotIn( |
|||
self.subtype1, self.partner1.disabled_notify_subtype_ids) |
|||
self.assertIn( |
|||
self.subtype2, self.partner1.enabled_notify_subtype_ids) |
|||
self.assertNotIn( |
|||
self.subtype2, self.partner1.disabled_notify_subtype_ids) |
|||
self.assertIn( |
|||
self.subtype3, self.partner1.disabled_notify_subtype_ids) |
|||
self.assertNotIn( |
|||
self.subtype3, self.partner1.enabled_notify_subtype_ids) |
|||
self.assertNotIn( |
|||
self.subtype4, |
|||
self.partner1.enabled_notify_subtype_ids) |
|||
self.assertNotIn( |
|||
self.subtype4, |
|||
self.partner1.disabled_notify_subtype_ids) |
|||
|
|||
def test_partner_find_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) |
|||
) |
|||
domain = [( |
|||
'disabled_notify_subtype_ids', 'in', self.subtype3.id, |
|||
)] |
|||
self.assertIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
domain = [( |
|||
'enabled_notify_subtype_ids', 'in', (self.subtype3.id, ), |
|||
)] |
|||
self.assertNotIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
domain = [( |
|||
'enabled_notify_subtype_ids', 'in', (self.subtype4.id, ), |
|||
)] |
|||
self.assertNotIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
domain = [( |
|||
'disabled_notify_subtype_ids', 'in', (self.subtype4.id, ), |
|||
)] |
|||
self.assertNotIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
|
|||
def test_partner_find_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) |
|||
) |
|||
domain = [( |
|||
'disabled_notify_subtype_ids', |
|||
'not in', (self.subtype4.id, ), |
|||
)] |
|||
self.assertIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
domain = [( |
|||
'enabled_notify_subtype_ids', |
|||
'not in', (self.subtype3.id, ), |
|||
)] |
|||
self.assertIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
|||
domain = [( |
|||
'disabled_notify_subtype_ids', |
|||
'not in', (self.subtype1.id, self.subtype2.id), |
|||
)] |
|||
self.assertIn( |
|||
self.partner1, |
|||
self.partner_model.search(domain) |
|||
) |
@ -0,0 +1,50 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<record model="ir.ui.view" id="mail_digest_tree"> |
|||
<field name="name">mail_digest mail.digest.tree</field> |
|||
<field name="model">mail.digest</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Mail digest"> |
|||
<field name="partner_id" /> |
|||
<field name="mail_id" /> |
|||
<field name="state" /> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
|
|||
<record model="ir.ui.view" id="mail_digest_form"> |
|||
<field name="name">mail_digest mail.digest.form</field> |
|||
<field name="model">mail.digest</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Mail digest"> |
|||
<group name="main" col="4"> |
|||
<field name="name" /> |
|||
<field name="partner_id" /> |
|||
<field name="frequency" /> |
|||
<field name="mail_id" /> |
|||
<field name="state" /> |
|||
</group> |
|||
<group name="messages" string="Messages" col="4"> |
|||
<field name="message_ids" nolabel="1" /> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="ir.actions.act_window" id="action_digest_all"> |
|||
<field name="name">Digest</field> |
|||
<field name="res_model">mail.digest</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">form,tree</field> |
|||
<field name="view_id" ref="mail_digest_tree" /> |
|||
</record> |
|||
|
|||
<menuitem id="menu_email_digest" parent="base.menu_email" |
|||
action="action_digest_all" |
|||
sequence="90"/> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,41 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<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> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,18 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<record id="notifications_email_user_info_form" model="ir.ui.view"> |
|||
<field name="name">mail.notifications res.users.form</field> |
|||
<field name="model">res.users</field> |
|||
<field name="inherit_id" ref="mail.view_users_form_mail"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//field[@name='notify_email']" position="after"> |
|||
<field name="notify_frequency" attrs="{'invisible': [('notify_email','=', 'none')]}" /> |
|||
<field name="notify_conf_ids" attrs="{'invisible': [('notify_email','=', 'none')]}" /> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
|
|||
</data> |
|||
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue