mirror of https://github.com/OCA/social.git
[add] mail_digest
parent
1516e65e64
commit
600648a9f0
|
@ -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 ""
|
Binary file not shown.
After Width: | Height: | 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
|
|
Binary file not shown.
After Width: | Height: | 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>
|
Loading…
Reference in New Issue