Merge PR #1312 into 17.0

Signed-off-by hbrunn
pull/1385/head
OCA-git-bot 2024-07-22 05:42:44 +00:00
commit a7d3b2be2f
59 changed files with 2909 additions and 0 deletions

View File

@ -0,0 +1,152 @@
================
Email CC and BCC
================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:254245c99d868f3a2c4cfdfec26f8d1be09fdc4b8bd2ec691e7f1db4cda9902b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github
:target: https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc
:alt: OCA/social
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/social-17-0/social-17-0-mail_composer_cc_bcc
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=17.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
Odoo native does not support defining a Cc field in the Mail Composer by
default; instead, it only has a unique Recipients fields, which is
confusing for a lot of end users.
This module allows to properly separate To:, Cc:, and Bcc: fields in the
Mail Composer.
Features
--------
- Add Cc and Bcc fields to mail composer form. Send only once to
multiple email addresses.
- Add Cc and Bcc fields to company form to use them as default in mail
composer form.
- Add Bcc field to mail template form. Use Cc and Bcc fields to lookup
partners by email then add them to corresponding fields in mail
composer form.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_
**Table of contents**
.. contents::
:local:
Configuration
=============
In company form there are two fields to set default cc and bcc partners.
|res_company_form_default_cc_bcc|
In template form there are two fields to set cc and bcc emails.
|email_template_form_cc_bcc|
.. |res_company_form_default_cc_bcc| image:: https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/res_company_form_default_cc_bcc.png
.. |email_template_form_cc_bcc| image:: https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/email_template_form_cc_bcc.png
Usage
=====
The partners cc and bcc from company form will be used to fill in mail
composer form.
|image|
When select a template that has cc and/or bcc emails, the emails will be
used to lookup partners then found partners will be added to
corresponding mail composer's fields.
|image1|
.. |image| image:: https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/mail_compose_message_default_cc_bcc.png
.. |image1| image:: https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/mail_compose_message_template_cc_bcc.png
Known issues / Roadmap
======================
- Extract account customization (account.invoice.send wizard) to a
specific module mail_composer_cc_bcc_account
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 to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/social/issues/new?body=module:%20mail_composer_cc_bcc%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Camptocamp
Contributors
------------
- `Trobz <https://www.trobz.com>`__:
- Hai N. Le <hailn@trobz.com>
- Son Ho <sonhd@trobz.com>
- Tri Doan <tridm@trobz.com>
Other credits
-------------
The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
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.
.. |maintainer-trisdoan| image:: https://github.com/trisdoan.png?size=40px
:target: https://github.com/trisdoan
:alt: trisdoan
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-trisdoan|
This module is part of the `OCA/social <https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1,2 @@
from . import models
from . import wizards

View File

@ -0,0 +1,26 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Email CC and BCC",
"summary": "This module enables sending mail to CC and BCC partners in mail composer form.", # noqa: E501
"version": "17.0.1.0.0",
"development_status": "Alpha",
"category": "Social",
"website": "https://github.com/OCA/social",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainers": ["trisdoan"],
"license": "AGPL-3",
"application": False,
"installable": True,
"preloadable": True,
"depends": [
"mail",
],
"data": [
"views/res_company_views.xml",
"views/mail_mail_views.xml",
"views/mail_message_views.xml",
"views/mail_template_views.xml",
"wizards/mail_compose_message_view.xml",
],
}

View File

@ -0,0 +1,127 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_composer_cc_bcc
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-11-08 13:02+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__email_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Bcc"
msgstr "Bcc"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_bcc_ids
msgid "Bcc (Partners)"
msgstr "Bcc (Socios)"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_mail__email_bcc
msgid "Blind Cc message recipients"
msgstr "Destinatarios de mensajes Cc ciegos"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Blind cc recipients (placeholders may be used here)"
msgstr "Destinatarios cc ciegos (pueden utilizarse marcadores de posición)"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_cc_ids
msgid "Cc"
msgstr "Con copia (cc)"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_cc_ids
msgid "Cc (Partners)"
msgstr "Cc (Socios)"
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.email_template_form_inherit
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Comma-separated blind carbon copy recipients addresses"
msgstr "Direcciones de los destinatarios de la copia oculta separadas por comas"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_res_company
msgid "Companies"
msgstr "Compañías"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_bcc_ids
msgid "Default Bcc"
msgstr "Bcc por defecto"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_cc_ids
msgid "Default Cc"
msgstr "Cc por defecto"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_template
msgid "Email Templates"
msgstr "Plantillas Correo Electrónico"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_thread
msgid "Email Thread"
msgstr "Hilo de Correo Electrónico"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Asistente de redacción de correo electrónico"
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do concurrent access update of "
"notification records. Please see with an administrator."
msgstr ""
"Error sin excepción. Probablemente debido a la actualización de acceso "
"concurrente de los registros de notificación. Por favor, consulte con un "
"administrador."
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do sending an email without computed "
"recipients."
msgstr ""
"Error sin excepción. Probablemente debido a enviar un email sin "
"destinatarios computados."
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_message
msgid "Message"
msgstr "Mensaje"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_mail
msgid "Outgoing Mails"
msgstr "Correos Salientes"
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Partners"
msgstr "Socios"

View File

@ -0,0 +1,126 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_composer_cc_bcc
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-12-07 16:33+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__email_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Bcc"
msgstr "Bcc"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_bcc_ids
msgid "Bcc (Partners)"
msgstr "Bcc (partner)"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_mail__email_bcc
msgid "Blind Cc message recipients"
msgstr "Nascondi destinatari in CC meggaggio"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Blind cc recipients (placeholders may be used here)"
msgstr "Nascondi destinatari in CC (qui si possono usare segnaposto)"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_cc_ids
msgid "Cc"
msgstr "Cc"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_cc_ids
msgid "Cc (Partners)"
msgstr "Cc (partner)"
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.email_template_form_inherit
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Comma-separated blind carbon copy recipients addresses"
msgstr "Indrizzi destinatari in copia carbone nascosta separati da virgola"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_res_company
msgid "Companies"
msgstr "Aziende"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_bcc_ids
msgid "Default Bcc"
msgstr "Bcc predefinito"
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_cc_ids
msgid "Default Cc"
msgstr "Cc predefinito"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_template
msgid "Email Templates"
msgstr "Modelli e-mail"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_thread
msgid "Email Thread"
msgstr "Discussione e-mail"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_compose_message
msgid "Email composition wizard"
msgstr "Procedura guidata creazione e-mail"
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do concurrent access update of "
"notification records. Please see with an administrator."
msgstr ""
"Errore senza eccezione. Probabilmente per un accesso concorrente per "
"aggiornamento record notifiche. Verificare con un amministratore."
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do sending an email without computed "
"recipients."
msgstr ""
"Errore senza eccezione. Probabilmente dovto all'invio di una e-mail con "
"destinatari mancanti."
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_message
msgid "Message"
msgstr "Messaggio"
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_mail
msgid "Outgoing Mails"
msgstr "E-mail in uscita"
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Partners"
msgstr "Partner"

View File

@ -0,0 +1,119 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_composer_cc_bcc
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \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_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__email_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Bcc"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_bcc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_bcc_ids
msgid "Bcc (Partners)"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_mail__email_bcc
msgid "Blind Cc message recipients"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,help:mail_composer_cc_bcc.field_mail_template__email_bcc
msgid "Blind cc recipients (placeholders may be used here)"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_account_invoice_send__partner_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_compose_message__partner_cc_ids
msgid "Cc"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_mail__recipient_cc_ids
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_mail_message__recipient_cc_ids
msgid "Cc (Partners)"
msgstr ""
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.email_template_form_inherit
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Comma-separated blind carbon copy recipients addresses"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_res_company
msgid "Companies"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_bcc_ids
msgid "Default Bcc"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model.fields,field_description:mail_composer_cc_bcc.field_res_company__default_partner_cc_ids
msgid "Default Cc"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_template
msgid "Email Templates"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_thread
msgid "Email Thread"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_compose_message
msgid "Email composition wizard"
msgstr ""
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do concurrent access update of "
"notification records. Please see with an administrator."
msgstr ""
#. module: mail_composer_cc_bcc
#. odoo-python
#: code:addons/mail_composer_cc_bcc/models/mail_mail.py:0
#, python-format
msgid ""
"Error without exception. Probably due do sending an email without computed "
"recipients."
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_message
msgid "Message"
msgstr ""
#. module: mail_composer_cc_bcc
#: model:ir.model,name:mail_composer_cc_bcc.model_mail_mail
msgid "Outgoing Mails"
msgstr ""
#. module: mail_composer_cc_bcc
#: model_terms:ir.ui.view,arch_db:mail_composer_cc_bcc.view_mail_form_inherit
msgid "Partners"
msgstr ""

View File

@ -0,0 +1,8 @@
# These modules are sorted by calling sequence, i.e. mail_thread calls
# mail_message, etc.
from . import res_company
from . import mail_template
from . import mail_thread
from . import mail_message
from . import ir_mail_server
from . import mail_mail

View File

@ -0,0 +1,28 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
from odoo import models
_logger = logging.getLogger(__name__)
class IrMailServer(models.Model):
_inherit = "ir.mail_server"
def _prepare_email_message(self, message, smtp_session):
"""
Define smtp_to based on context instead of To+Cc+Bcc
"""
smtp_from, smtp_to_list, message = super()._prepare_email_message(
message, smtp_session
)
is_from_composer = self.env.context.get("is_from_composer", False)
if is_from_composer and self.env.context.get("recipients", False):
smtp_to = self.env.context["recipients"].pop(0)
_logger.debug("smtp_to: %s", smtp_to)
smtp_to_list = [smtp_to]
return smtp_from, smtp_to_list, message

View File

@ -0,0 +1,78 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models, tools
from odoo.addons.base.models.ir_mail_server import extract_rfc2822_addresses
def format_emails(partners):
emails = [tools.formataddr((p.name or "", p.email)) for p in partners if p.email]
return ", ".join(emails)
def format_emails_raw(partners):
emails = [p.email for p in partners if p.email]
return ", ".join(emails)
class MailMail(models.Model):
_inherit = "mail.mail"
email_bcc = fields.Char("Bcc", help="Blind Cc message recipients")
def _prepare_outgoing_list(self, recipients_follower_status=None):
# First, return if we're not coming from the Mail Composer
res = super()._prepare_outgoing_list(
recipients_follower_status=recipients_follower_status
)
is_out_of_scope = len(self.ids) > 1
is_from_composer = self.env.context.get("is_from_composer", False)
if is_out_of_scope or not is_from_composer:
return res
# Prepare values for To, Cc headers
partners_cc_bcc = self.recipient_cc_ids + self.recipient_bcc_ids
partner_to_ids = [r.id for r in self.recipient_ids if r not in partners_cc_bcc]
partner_to = self.env["res.partner"].browse(partner_to_ids)
email_to = format_emails(partner_to)
email_to_raw = format_emails_raw(partner_to)
email_cc = format_emails(self.recipient_cc_ids)
email_bcc = [r.email for r in self.recipient_bcc_ids if r.email]
# Collect recipients (RCPT TO) and update all emails
# with the same To, Cc headers (to be shown by email client as users expect)
recipients = []
for m in res:
rcpt_to = None
if m["email_to"]:
rcpt_to = extract_rfc2822_addresses(m["email_to"][0])[0]
# If the recipient is a Bcc, we had an explicit header X-Odoo-Bcc
# - It won't be shown by the email client, but can be useful for a recipient # noqa: E501
# to understand why he received a given email
# - Also note that in python3, the smtp.send_message method does not
# transmit the Bcc field of a Message object
if rcpt_to in email_bcc:
m["headers"].update({"X-Odoo-Bcc": m["email_to"][0]})
# in the absence of self.email_to, Odoo creates one special mail for CC
# see https://github.com/odoo/odoo/commit/46bad8f0
elif m["email_cc"]:
rcpt_to = extract_rfc2822_addresses(m["email_cc"][0])[0]
if rcpt_to:
recipients.append(rcpt_to)
m.update(
{
"email_to": email_to,
"email_to_raw": email_to_raw,
"email_cc": email_cc,
}
)
self.env.context = {**self.env.context, "recipients": recipients}
return res

View File

@ -0,0 +1,25 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class MailMessage(models.Model):
_inherit = "mail.message"
recipient_cc_ids = fields.Many2many(
"res.partner",
"mail_message_res_partner_cc_rel",
"mail_message_id",
"parent_id",
string="Cc (Partners)",
context={"active_test": False},
)
recipient_bcc_ids = fields.Many2many(
"res.partner",
"mail_message_res_partner_bcc_rel",
"mail_message_id",
"parent_id",
string="Bcc (Partners)",
context={"active_test": False},
)

View File

@ -0,0 +1,201 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import itertools
from odoo import fields, models, tools
class MailTemplate(models.Model):
_inherit = "mail.template"
email_bcc = fields.Char(
"Bcc", help="Blind cc recipients (placeholders may be used here)"
)
# ------------------------------------------------------------
# MESSAGE/EMAIL VALUES GENERATION
# ------------------------------------------------------------
def _generate_template_recipients( # noqa:C901
self, res_ids, render_fields, find_or_create_partners=False, render_results=None
):
self.ensure_one()
if render_results is None:
render_results = {}
ModelSudo = self.env[self.model].with_prefetch(res_ids).sudo()
# if using default recipients -> ``_message_get_default_recipients`` gives
# values for email_to, email_cc and partner_ids
if self.use_default_to and self.model:
default_recipients = ModelSudo.browse(
res_ids
)._message_get_default_recipients()
for res_id, recipients in default_recipients.items():
render_results.setdefault(res_id, {}).update(recipients)
# render fields dynamically which generates recipients
else:
for field in set(render_fields) & {
"email_cc",
"email_to",
"partner_to",
"email_bcc",
}:
generated_field_values = self._render_field(field, res_ids)
for res_id in res_ids:
render_results.setdefault(res_id, {})[
field
] = generated_field_values[res_id]
# create partners from emails if asked to
if find_or_create_partners:
res_id_to_company = {}
if self.model and "company_id" in ModelSudo._fields:
for read_record in ModelSudo.browse(res_ids).read(["company_id"]):
company_id = (
read_record["company_id"][0]
if read_record["company_id"]
else False
)
res_id_to_company[read_record["id"]] = company_id
all_emails = []
email_to_res_ids = {}
email_to_company = {}
for res_id in res_ids:
record_values = render_results.setdefault(res_id, {})
# DIFFERENT FROM ODOO NATIVE:
if record_values.get("email_cc"):
continue
mails = tools.email_split(record_values.pop("email_to", ""))
all_emails += mails
record_company = res_id_to_company.get(res_id)
for mail in mails:
email_to_res_ids.setdefault(mail, []).append(res_id)
if record_company:
email_to_company[mail] = record_company
if all_emails:
customers_information = ModelSudo.browse(
res_ids
)._get_customer_information()
partners = self.env["res.partner"]._find_or_create_from_emails(
all_emails,
additional_values={
email: {
"company_id": email_to_company.get(email),
**customers_information.get(email, {}),
}
for email in itertools.chain(all_emails, [False])
},
)
for original_email, partner in zip(all_emails, partners): # noqa: B905
if not partner:
continue
for res_id in email_to_res_ids[original_email]:
render_results[res_id].setdefault("partner_ids", []).append(
partner.id
)
# update 'partner_to' rendered value to 'partner_ids'
all_partner_to = {
pid
for record_values in render_results.values()
for pid in self._parse_partner_to(record_values.get("partner_to", ""))
}
existing_pids = set()
if all_partner_to:
existing_pids = set(
self.env["res.partner"].sudo().browse(list(all_partner_to)).exists().ids
)
for res_id, record_values in render_results.items(): # noqa: B007
partner_to = record_values.pop("partner_to", "")
if partner_to:
tpl_partner_ids = (
set(self._parse_partner_to(partner_to)) & existing_pids
)
record_values.setdefault("partner_ids", []).extend(tpl_partner_ids)
# DIFFERENT FROM ODOO NATIVE:
# update 'email_cc' rendered value to 'partner_bcc_ids'
all_cc_emails = []
if record_values.get("email_cc", ""):
mails = tools.email_split(record_values.pop("email_cc", ""))
all_cc_emails += mails
record_company = res_id_to_company.get(res_id)
for mail in mails:
email_to_res_ids.setdefault(mail, []).append(res_id)
if record_company:
email_to_company[mail] = record_company
# DIFFERENT FROM ODOO NATIVE:
if all_cc_emails:
customers_information = ModelSudo.browse(
res_ids
)._get_customer_information()
partners = self.env["res.partner"]._find_or_create_from_emails(
all_cc_emails,
additional_values={
email: {
"company_id": email_to_company.get(email),
**customers_information.get(email, {}),
}
for email in itertools.chain(all_cc_emails, [False])
},
)
for original_email, partner in zip(all_cc_emails, partners): # noqa: B905
if not partner:
continue
for res_id in email_to_res_ids[original_email]:
render_results[res_id].setdefault("partner_cc_ids", []).append(
partner.id
)
# DIFFERENT FROM ODOO NATIVE:
# update 'email_bcc' rendered value to 'partner_bcc_ids'
all_bcc_emails = []
if record_values.get("email_bcc", ""):
mails = tools.email_split(record_values.pop("email_bcc", ""))
all_bcc_emails += mails
record_company = res_id_to_company.get(res_id)
for mail in mails:
email_to_res_ids.setdefault(mail, []).append(res_id)
if record_company:
email_to_company[mail] = record_company
# DIFFERENT FROM ODOO NATIVE:
if all_bcc_emails:
customers_information = ModelSudo.browse(
res_ids
)._get_customer_information()
partners = self.env["res.partner"]._find_or_create_from_emails(
all_bcc_emails,
additional_values={
email: {
"company_id": email_to_company.get(email),
**customers_information.get(email, {}),
}
for email in itertools.chain(all_bcc_emails, [False])
},
)
for original_email, partner in zip(all_bcc_emails, partners): # noqa: B905
if not partner:
continue
for res_id in email_to_res_ids[original_email]:
render_results[res_id].setdefault("partner_bcc_ids", []).append(
partner.id
)
return render_results
def _generate_template(self, res_ids, render_fields, find_or_create_partners=False):
res = super()._generate_template(
res_ids, render_fields, find_or_create_partners=find_or_create_partners
)
for _, (template, template_res_ids) in self._classify_per_lang(res_ids).items():
if "email_bcc" in render_fields:
template._generate_template_recipients(
template_res_ids,
set("email_bcc"),
render_results=res,
find_or_create_partners=find_or_create_partners,
)
return res

View File

@ -0,0 +1,114 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
from .mail_mail import format_emails
class MailThread(models.AbstractModel):
_inherit = "mail.thread"
# ------------------------------------------------------------
# MAIL.MESSAGE HELPERS
# ------------------------------------------------------------
def _get_message_create_valid_field_names(self):
"""
add cc and bcc field to create record in mail.mail
"""
field_names = super()._get_message_create_valid_field_names()
field_names.update({"recipient_cc_ids", "recipient_bcc_ids"})
return field_names
# ------------------------------------------------------
# NOTIFICATION API
# ------------------------------------------------------
def _notify_by_email_get_base_mail_values(self, message, additional_values=None):
"""
This is to add cc, bcc addresses to mail.mail objects so that email
can be sent to those addresses.
"""
res = super()._notify_by_email_get_base_mail_values(
message, additional_values=additional_values
)
context = self.env.context
partners_cc = context.get("partner_cc_ids", None)
if partners_cc:
res["email_cc"] = format_emails(partners_cc)
partners_bcc = context.get("partner_bcc_ids", None)
if partners_bcc:
res["email_bcc"] = format_emails(partners_bcc)
return res
def _notify_get_recipients(self, message, msg_vals, **kwargs):
"""
This is to add cc, bcc recipients so that they can be grouped with
other recipients.
"""
ResPartner = self.env["res.partner"]
MailFollowers = self.env["mail.followers"]
rdata = super()._notify_get_recipients(message, msg_vals, **kwargs)
context = self.env.context
is_from_composer = context.get("is_from_composer", False)
if not is_from_composer:
return rdata
for pdata in rdata:
pdata["type"] = "customer"
partners_cc_bcc = context.get("partner_cc_ids", ResPartner)
partners_cc_bcc += context.get("partner_bcc_ids", ResPartner)
msg_sudo = message.sudo()
message_type = (
msg_vals.get("message_type") if msg_vals else msg_sudo.message_type
)
subtype_id = msg_vals.get("subtype_id") if msg_vals else msg_sudo.subtype_id.id
recipients_cc_bcc = MailFollowers._get_recipient_data(
None, message_type, subtype_id, partners_cc_bcc.ids
)
for _, value in recipients_cc_bcc.items():
for _, data in value.items():
if not data.get("id"):
continue
if not data.get(
"notif"
): # notif is False, has no user, is therefore customer
notif = "email"
msg_type = "customer"
pdata = {
"id": data.get("id"),
"active": data.get("active"),
"share": data.get("share"),
"notif": data.get("notif") and data.get("notif") or notif,
"type": msg_type,
"is_follower": data.get("is_follower"),
}
rdata.append(pdata)
return rdata
def _notify_get_recipients_classify(
self, message, recipients_data, model_description, msg_vals=None
):
res = super()._notify_get_recipients_classify(
message, recipients_data, model_description, msg_vals=msg_vals
)
is_from_composer = self.env.context.get("is_from_composer", False)
if not is_from_composer:
return res
ids = []
customer_data = None
for rcpt_data in res:
if rcpt_data["notification_group_name"] == "customer":
customer_data = rcpt_data
else:
ids += rcpt_data["recipients"]
if not customer_data:
customer_data = res[0]
customer_data["notification_group_name"] = "customer"
customer_data["recipients"] = ids
else:
customer_data["recipients"] += ids
return [customer_data]

View File

@ -0,0 +1,23 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class Company(models.Model):
_inherit = "res.company"
default_partner_cc_ids = fields.Many2many(
"res.partner",
"res_company_res_partner_cc_rel",
"company_id",
"partner_id",
string="Default Cc",
)
default_partner_bcc_ids = fields.Many2many(
"res.partner",
"res_company_res_partner_bcc_rel",
"company_id",
"partner_id",
string="Default Bcc",
)

View File

@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@ -0,0 +1,7 @@
In company form there are two fields to set default cc and bcc partners.
> ![res_company_form_default_cc_bcc](../static/img/res_company_form_default_cc_bcc.png)
In template form there are two fields to set cc and bcc emails.
> ![email_template_form_cc_bcc](../static/img/email_template_form_cc_bcc.png)

View File

@ -0,0 +1,5 @@
- [Trobz](https://www.trobz.com):
> - Hai N. Le \<<hailn@trobz.com>\>
> - Son Ho \<<sonhd@trobz.com>\>
> - Tri Doan \<<tridm@trobz.com>\>

View File

@ -0,0 +1,2 @@
The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.

View File

@ -0,0 +1,16 @@
Odoo native does not support defining a Cc field in the Mail Composer by
default; instead, it only has a unique Recipients fields, which is
confusing for a lot of end users.
This module allows to properly separate To:, Cc:, and Bcc: fields in the
Mail Composer.
## Features
- Add Cc and Bcc fields to mail composer form. Send only once to
multiple email addresses.
- Add Cc and Bcc fields to company form to use them as default in mail
composer form.
- Add Bcc field to mail template form. Use Cc and Bcc fields to lookup
partners by email then add them to corresponding fields in mail
composer form.

View File

@ -0,0 +1,2 @@
- Extract account customization (account.invoice.send wizard) to a
specific module mail_composer_cc_bcc_account

View File

@ -0,0 +1,10 @@
The partners cc and bcc from company form will be used to fill in mail
composer form.
> ![image](../static/img/mail_compose_message_default_cc_bcc.png)
When select a template that has cc and/or bcc emails, the emails will be
used to lookup partners then found partners will be added to
corresponding mail composer's fields.
> ![image](../static/img/mail_compose_message_template_cc_bcc.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,486 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Email CC and BCC</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="email-cc-and-bcc">
<h1 class="title">Email CC and BCC</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:254245c99d868f3a2c4cfdfec26f8d1be09fdc4b8bd2ec691e7f1db4cda9902b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc"><img alt="OCA/social" src="https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/social-17-0/social-17-0-mail_composer_cc_bcc"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/social&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Odoo native does not support defining a Cc field in the Mail Composer by
default; instead, it only has a unique Recipients fields, which is
confusing for a lot of end users.</p>
<p>This module allows to properly separate To:, Cc:, and Bcc: fields in the
Mail Composer.</p>
<div class="section" id="features">
<h1>Features</h1>
<ul class="simple">
<li>Add Cc and Bcc fields to mail composer form. Send only once to
multiple email addresses.</li>
<li>Add Cc and Bcc fields to company form to use them as default in mail
composer form.</li>
<li>Add Bcc field to mail template form. Use Cc and Bcc fields to lookup
partners by email then add them to corresponding fields in mail
composer form.</li>
</ul>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-3">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-5">Credits</a></li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>In company form there are two fields to set default cc and bcc partners.</p>
<blockquote>
<img alt="res_company_form_default_cc_bcc" src="https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/res_company_form_default_cc_bcc.png" /></blockquote>
<p>In template form there are two fields to set cc and bcc emails.</p>
<blockquote>
<img alt="email_template_form_cc_bcc" src="https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/email_template_form_cc_bcc.png" /></blockquote>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<p>The partners cc and bcc from company form will be used to fill in mail
composer form.</p>
<blockquote>
<img alt="image" src="https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/mail_compose_message_default_cc_bcc.png" /></blockquote>
<p>When select a template that has cc and/or bcc emails, the emails will be
used to lookup partners then found partners will be added to
corresponding mail composers fields.</p>
<blockquote>
<img alt="image1" src="https://raw.githubusercontent.com/OCA/social/17.0/mail_composer_cc_bcc/static/img/mail_compose_message_template_cc_bcc.png" /></blockquote>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h2>
<ul class="simple">
<li>Extract account customization (account.invoice.send wizard) to a
specific module mail_composer_cc_bcc_account</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/social/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/social/issues/new?body=module:%20mail_composer_cc_bcc%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
</div>
</div>
<div class="section" id="authors">
<h1>Authors</h1>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h1>Contributors</h1>
<ul>
<li><p class="first"><a class="reference external" href="https://www.trobz.com">Trobz</a>:</p>
<blockquote>
<ul class="simple">
<li>Hai N. Le &lt;<a class="reference external" href="mailto:hailn&#64;trobz.com">hailn&#64;trobz.com</a>&gt;</li>
<li>Son Ho &lt;<a class="reference external" href="mailto:sonhd&#64;trobz.com">sonhd&#64;trobz.com</a>&gt;</li>
<li>Tri Doan &lt;<a class="reference external" href="mailto:tridm&#64;trobz.com">tridm&#64;trobz.com</a>&gt;</li>
</ul>
</blockquote>
</li>
</ul>
</div>
<div class="section" id="other-credits">
<h1>Other credits</h1>
<p>The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.</p>
</div>
<div class="section" id="maintainers">
<h1>Maintainers</h1>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/trisdoan"><img alt="trisdoan" src="https://github.com/trisdoan.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc">OCA/social</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1 @@
from . import test_mail_cc_bcc

View File

@ -0,0 +1,205 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import hashlib
import inspect
from odoo import tools
from odoo.tests import Form
from odoo.addons.mail.models.mail_template import MailTemplate as MailTemplate_upstream
from odoo.addons.mail.tests.test_mail_composer import TestMailComposerForm
from odoo.addons.mail.wizard.mail_compose_message import (
MailComposer as MailComposer_upstream,
)
VALID_HASHES = {
"mail.template:_generate_template_recipients": ["73b0e20a018984841e454a57a86ee08d"],
"mail.composer:_compute_partner_ids": ["813ef112e3948fe625b9a89428f2518d"],
}
class TestMailCcBcc(TestMailComposerForm):
@classmethod
def setUpClass(cls):
super().setUpClass()
env = cls.env
cls.partner = env.ref("base.res_partner_address_31")
cls.partner_cc = env.ref("base.partner_demo")
cls.partner_cc2 = env.ref("base.partner_demo_portal")
cls.partner_cc3 = env.ref("base.res_partner_main1")
cls.partner_bcc = env.ref("base.res_partner_main2")
def open_mail_composer_form(self):
# Use form to populate data
test_record = self.test_record.with_env(self.env)
ctx = {
"default_partner_ids": test_record.ids,
"default_model": test_record._name,
"default_res_ids": test_record.ids,
# to ensure consistent test results even when mail_post_defer is installed
"mail_notify_force_send": True,
}
form = Form(self.env["mail.compose.message"].with_context(**ctx))
form.body = "<p>Hello</p>"
return form
def test_MailTemplate_upstream_file_hash(self):
"""Test that copied upstream function hasn't received fixes"""
func = inspect.getsource(
MailTemplate_upstream._generate_template_recipients
).encode()
func_hash = hashlib.md5(func).hexdigest()
self.assertIn(
func_hash, VALID_HASHES.get("mail.template:_generate_template_recipients")
)
def test_MailComposer_upstream_file_hash(self):
"""Test that copied upstream function hasn't received fixes"""
_compute_partner_ids = inspect.getsource(
MailComposer_upstream._compute_partner_ids
).encode()
func_hash = hashlib.md5(_compute_partner_ids).hexdigest()
self.assertIn(func_hash, VALID_HASHES.get("mail.composer:_compute_partner_ids"))
def test_email_cc_bcc(self):
form = self.open_mail_composer_form()
composer = form.save()
# Use object to update Many2many fields (form can't do like this)
composer.partner_cc_ids = self.partner_cc
composer.partner_cc_ids |= self.partner_cc2
composer.partner_cc_ids |= self.partner_cc3
composer.partner_bcc_ids = self.partner_bcc
with self.mock_mail_gateway():
composer._action_send_mail()
# Verify recipients of mail.message
message = self.test_record.message_ids[0]
self.assertEqual(len(message.recipient_cc_ids), 3)
self.assertEqual(len(message.recipient_bcc_ids), 1)
# Verify notification
for_message = [
("mail_message_id", "=", message.id),
("notification_type", "=", "email"),
]
notif = self.env["mail.notification"].search(for_message)
self.assertEqual(len(notif), 5)
# Verify data of mail.mail
mail = message.mail_ids
expecting = ", ".join(
[
'"Marc Demo" <mark.brown23@example.com>',
'"Joel Willis" <joel.willis63@example.com>',
'"Chester Reed" <chester.reed79@example.com>',
]
)
self.assertEqual(mail.email_cc, expecting)
expecting = '"Dwayne Newman" <dwayne.newman28@example.com>'
self.assertEqual(mail.email_bcc, expecting)
def test_template_cc_bcc(self):
env = self.env
# Company default values
env.company.default_partner_cc_ids = self.partner_cc3
env.company.default_partner_bcc_ids = self.partner_cc2
# Partner template values
tmpl_model = env["ir.model"].search([("model", "=", "res.partner")])
partner_cc = self.partner_cc
partner_bcc = self.partner_bcc
vals = {
"name": "Product Template: Re: [E-COM11] Cabinet with Doors",
"model_id": tmpl_model.id,
"subject": "Re: [E-COM11] Cabinet with Doors",
"body_html": """<p style="margin:0px 0 12px 0;box-sizing:border-box;">
Test Template<br></p>""",
"email_cc": tools.formataddr(
(partner_cc.name or "False", partner_cc.email or "False")
),
"email_bcc": tools.formataddr(
(partner_bcc.name or "False", partner_bcc.email or "False")
),
}
partner_tmpl = env["mail.template"].create(vals)
# Open mail composer form and check for default values from company
form = self.open_mail_composer_form()
composer = form.save()
self.assertEqual(composer.partner_cc_ids, self.partner_cc3)
self.assertEqual(composer.partner_bcc_ids, self.partner_cc2)
# Change email template and check for values from it
form.template_id = partner_tmpl
composer = form.save()
# Beside existing Cc and Bcc, add template's ones
form = Form(composer)
form.template_id = partner_tmpl
composer = form.save()
expecting = self.partner_cc3 + self.partner_cc
self.assertEqual(composer.partner_cc_ids, expecting)
expecting = self.partner_cc2 + self.partner_bcc
self.assertEqual(composer.partner_bcc_ids, expecting)
# But not add Marc Demo from cc field to partner_ids field
self.assertEqual(len(composer.partner_ids), 1)
self.assertEqual(composer.partner_ids.display_name, "Test")
# Selecting the template again doesn't add as the partners already
# in the list
form = Form(composer)
form.template_id = env["mail.template"]
form.save()
self.assertFalse(form.template_id) # no template
form.template_id = partner_tmpl
composer = form.save()
expecting = self.partner_cc3 + self.partner_cc
self.assertEqual(composer.partner_cc_ids, expecting)
expecting = self.partner_cc2 + self.partner_bcc
self.assertEqual(composer.partner_bcc_ids, expecting)
def set_company(self):
company = self.env.company
# Company default values
company.default_partner_cc_ids = self.partner_cc3
company.default_partner_bcc_ids = self.partner_cc2
def test_recipient_ids_and_cc_bcc(self):
self.set_company()
form = self.open_mail_composer_form()
composer = form.save()
composer.partner_ids = self.partner + self.partner_cc
with self.mock_mail_gateway():
composer._action_send_mail()
message = self.test_record.message_ids[0]
self.assertEqual(len(message.mail_ids), 1)
# Only 4 partners notified
self.assertEqual(len(message.notified_partner_ids), 4)
self.assertEqual(len(message.notification_ids), 4)
def test_mail_without_cc_bcc(self):
self.set_company()
form = self.open_mail_composer_form()
subject = "Testing without cc/bcc single mail"
form.subject = subject
composer = form.save()
composer.partner_cc_ids = None
composer.partner_bcc_ids = None
composer.partner_ids = self.partner + self.partner_cc # 2 emails are sent
ctx = {"mail_notify_force_send": True}
ctx.update(composer.env.context)
composer = composer.with_context(**ctx)
with self.mock_mail_gateway():
composer._action_send_mail()
sent_mails = 0
for mail in self._mails:
if subject == mail.get("subject"):
sent_mails += 1
self.assertEqual(sent_mails, 2, "There should be 2 mails sent")

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="view_mail_form_inherit">
<field name="name">email.mail.form.inherit</field>
<field name="model">mail.mail</field>
<field name="inherit_id" ref="mail.view_mail_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='email_cc']" position="after">
<field
name="email_bcc"
placeholder="Comma-separated blind carbon copy recipients addresses"
/>
</xpath>
<xpath expr="//field[@name='recipient_ids']" position="attributes">
<attribute name="string">Partners</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="mail_message_view_form_inherit">
<field name="name">mail.message.form.inherit</field>
<field name="model">mail.message</field>
<field name="inherit_id" ref="mail.mail_message_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_ids']" position="after">
<field
name="recipient_cc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
/>
<field
name="recipient_bcc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="email_template_form_inherit">
<field name="name">email.template.form.inherit</field>
<field name="model">mail.template</field>
<field name="inherit_id" ref="mail.email_template_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='email_cc']" position="after">
<field
name="email_bcc"
invisible="use_default_to == True"
placeholder="Comma-separated blind carbon copy recipients addresses"
/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="view_company_form_inherit">
<field name="name">res.company.form.inherit</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='email']" position="after">
<field
name="default_partner_cc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
/>
<field
name="default_partner_bcc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1 @@
from . import mail_compose_message

View File

@ -0,0 +1,147 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import Command, api, fields, models
class MailComposeMessage(models.TransientModel):
_inherit = "mail.compose.message"
partner_cc_ids = fields.Many2many(
"res.partner",
"mail_compose_message_res_partner_cc_rel",
"wizard_id",
"partner_id",
string="Cc",
compute="_compute_partner_cc_bcc_ids",
readonly=False,
store=True,
)
partner_bcc_ids = fields.Many2many(
"res.partner",
"mail_compose_message_res_partner_bcc_rel",
"wizard_id",
"partner_id",
string="Bcc",
compute="_compute_partner_cc_bcc_ids",
readonly=False,
store=True,
)
# ------------------------------------------------------------
# SET DEFAULT VALUES FOR CC, BCC
# ------------------------------------------------------------
@api.model
def default_get(self, fields_list):
company = self.env.company
res = super().default_get(fields_list)
partner_cc = company.default_partner_cc_ids
if partner_cc:
res["partner_cc_ids"] = [Command.set(partner_cc.ids)]
partner_bcc = company.default_partner_bcc_ids
if partner_bcc:
res["partner_bcc_ids"] = [Command.set(partner_bcc.ids)]
return res
# TODO: reduce repeated code
@api.depends(
"composition_mode", "model", "parent_id", "res_domain", "res_ids", "template_id"
)
def _compute_partner_cc_bcc_ids(self):
for composer in self:
if (
composer.template_id
and composer.composition_mode == "comment"
and not composer.composition_batch
):
res_ids = composer._evaluate_res_ids() or [0]
rendered_values = composer._generate_template_for_composer(
res_ids,
{"email_cc", "email_bcc"},
find_or_create_partners=True,
)[res_ids[0]]
if rendered_values.get("partner_cc_ids"):
partner_cc_ids = rendered_values.get("partner_cc_ids")
if not isinstance(partner_cc_ids, list):
partner_cc_ids = [partner_cc_ids]
for partner_cc_id in partner_cc_ids:
composer.partner_cc_ids = [(4, partner_cc_id)]
if rendered_values.get("partner_bcc_ids"):
partner_bcc_ids = rendered_values.get("partner_bcc_ids")
if not isinstance(partner_bcc_ids, list):
partner_bcc_ids = [partner_bcc_ids]
for partner_bcc_id in partner_bcc_ids:
composer.partner_bcc_ids = [(4, partner_bcc_id)]
elif composer.parent_id and composer.composition_mode == "comment":
composer.partner_cc_ids = composer.parent_id.partner_cc_ids
composer.partner_bcc_ids = composer.parent_id.partner_bcc_ids
elif not composer.template_id:
composer.partner_cc_ids = self.env.company.default_partner_cc_ids
composer.partner_bcc_ids = self.env.company.default_partner_bcc_ids
@api.depends(
"composition_mode", "model", "parent_id", "res_domain", "res_ids", "template_id"
)
def _compute_partner_ids(self):
"""
Change: dont add email_cc to partner_ids
return: field Recipients filled with value from 'email_to', 'partner_ids'
"""
for composer in self:
if (
composer.template_id
and composer.composition_mode == "comment"
and not composer.composition_batch
):
res_ids = composer._evaluate_res_ids() or [0]
rendered_values = composer._generate_template_for_composer(
res_ids,
# DIFFERENT FROM ODOO NATIVE:
{"email_to", "partner_ids"},
find_or_create_partners=True,
)[res_ids[0]]
if rendered_values.get("partner_ids"):
composer.partner_ids = rendered_values["partner_ids"]
elif composer.parent_id and composer.composition_mode == "comment":
composer.partner_ids = composer.parent_id.partner_ids
elif not composer.template_id:
composer.partner_ids = False
# ------------------------------------------------------------
# RENDERING / VALUES GENERATION
# ------------------------------------------------------------
def _prepare_mail_values_rendered(self, res_ids):
"""
add cc and bcc when send to mail.message
"""
mail_values = super()._prepare_mail_values_rendered(res_ids)
for res_id in mail_values:
mail_values[res_id].update(
{
"recipient_cc_ids": self.partner_cc_ids.ids,
"recipient_bcc_ids": self.partner_bcc_ids.ids,
}
)
return mail_values
# ------------------------------------------------------------
# ACTIONS
# ------------------------------------------------------------
def _action_send_mail_comment(self, res_ids):
"""Add context is_from_composer"""
self.ensure_one()
context = {
"is_from_composer": True,
"partner_cc_ids": self.partner_cc_ids,
"partner_bcc_ids": self.partner_bcc_ids,
}
self = self.with_context(**context)
return super()._action_send_mail_comment(res_ids)

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form">
<field name="name">mail.compose.message.form.inherit</field>
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_ids']/.." position="after">
<field
name="partner_cc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
invisible="composition_mode != 'comment' or subtype_is_log == True"
/>
<field
name="partner_bcc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
invisible="composition_mode != 'comment' or subtype_is_log == True"
/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,116 @@
=====================================
Email CC and BCC when sending invoice
=====================================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:15899c467350be27c0af05844a1f2b429372cc738d36b456aa4162f766902c82
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github
:target: https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc_account
:alt: OCA/social
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/social-17-0/social-17-0-mail_composer_cc_bcc_account
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=17.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to properly separate To:, Cc:, and Bcc: fields in
when sending invoices.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_
**Table of contents**
.. contents::
:local:
Configuration
=============
This module will be automatically installed if both account and
mail_compose_cc_bcc are installed.
Usage
=====
Use the cc and bcc fields when sending invoices, to notify other
partners then the invoice contact of the customer.
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 to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/social/issues/new?body=module:%20mail_composer_cc_bcc_account%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Camptocamp SA
Contributors
------------
- `Trobz <https://www.trobz.com>`__:
- Hai N. Le <hailn@trobz.com>
- Son Ho <sonhd@trobz.com>
- Tris Doan <tridm@trobz.com>
- `Therp BV <https://therp.nl>`__:
- Ronald Portier <ronald@therp.nl>
Other credits
-------------
The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
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.
.. |maintainer-hailangvn2023| image:: https://github.com/hailangvn2023.png?size=40px
:target: https://github.com/hailangvn2023
:alt: hailangvn2023
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-hailangvn2023|
This module is part of the `OCA/social <https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc_account>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1,5 @@
# Copyright 2024 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models
from . import wizards

View File

@ -0,0 +1,24 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Email CC and BCC when sending invoice",
"summary": "This module enables sending mail to CC and BCC partners for invoices.",
"version": "17.0.1.0.0",
"development_status": "Alpha",
"category": "Social",
"website": "https://github.com/OCA/social",
"author": "Camptocamp SA, Odoo Community Association (OCA)",
"maintainers": ["hailangvn2023"],
"license": "AGPL-3",
"application": False,
"installable": True,
"auto_install": True,
"preloadable": True,
"depends": [
"account",
"mail_composer_cc_bcc",
],
"data": [
"wizards/account_move_send.xml",
],
}

View File

@ -0,0 +1,14 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"

View File

@ -0,0 +1,13 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \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"

View File

@ -0,0 +1 @@
from . import mail_thread

View File

@ -0,0 +1,19 @@
# Copyright 2024 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class MailThread(models.AbstractModel):
_inherit = "mail.thread"
def _message_create(self, values_list):
context = self.env.context
res = super()._message_create(values_list)
partners_cc = context.get("partner_cc_ids", None)
if partners_cc:
res.recipient_cc_ids = partners_cc
partners_bcc = context.get("partner_bcc_ids", None)
if partners_bcc:
res.recipient_bcc_ids = partners_bcc
return res

View File

@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@ -0,0 +1 @@
This module will be automatically installed if both account and mail_compose_cc_bcc are installed.

View File

@ -0,0 +1,9 @@
- [Trobz](https://www.trobz.com):
> - Hai N. Le \<<hailn@trobz.com>\>
> - Son Ho \<<sonhd@trobz.com>\>
> - Tris Doan \<<tridm@trobz.com>\>
- [Therp BV](https://therp.nl):
> - Ronald Portier \<<ronald@therp.nl>\>

View File

@ -0,0 +1,2 @@
The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.

View File

@ -0,0 +1,2 @@
This module allows to properly separate To:, Cc:, and Bcc: fields in
when sending invoices.

View File

@ -0,0 +1 @@
Use the cc and bcc fields when sending invoices, to notify other partners then the invoice contact of the customer.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,465 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Email CC and BCC when sending invoice</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="email-cc-and-bcc-when-sending-invoice">
<h1 class="title">Email CC and BCC when sending invoice</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:15899c467350be27c0af05844a1f2b429372cc738d36b456aa4162f766902c82
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc_account"><img alt="OCA/social" src="https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/social-17-0/social-17-0-mail_composer_cc_bcc_account"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/social&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to properly separate To:, Cc:, and Bcc: fields in
when sending invoices.</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#other-credits" id="toc-entry-7">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-8">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>This module will be automatically installed if both account and
mail_compose_cc_bcc are installed.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>Use the cc and bcc fields when sending invoices, to notify other
partners then the invoice contact of the customer.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/social/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/social/issues/new?body=module:%20mail_composer_cc_bcc_account%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Camptocamp SA</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul>
<li><p class="first"><a class="reference external" href="https://www.trobz.com">Trobz</a>:</p>
<blockquote>
<ul class="simple">
<li>Hai N. Le &lt;<a class="reference external" href="mailto:hailn&#64;trobz.com">hailn&#64;trobz.com</a>&gt;</li>
<li>Son Ho &lt;<a class="reference external" href="mailto:sonhd&#64;trobz.com">sonhd&#64;trobz.com</a>&gt;</li>
<li>Tris Doan &lt;<a class="reference external" href="mailto:tridm&#64;trobz.com">tridm&#64;trobz.com</a>&gt;</li>
</ul>
</blockquote>
</li>
<li><p class="first"><a class="reference external" href="https://therp.nl">Therp BV</a>:</p>
<blockquote>
<ul class="simple">
<li>Ronald Portier &lt;<a class="reference external" href="mailto:ronald&#64;therp.nl">ronald&#64;therp.nl</a>&gt;</li>
</ul>
</blockquote>
</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#toc-entry-7">Other credits</a></h2>
<p>The creation and migration from 16.0 to 17.0 of this module were
financially supported by Camptocamp.</p>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/hailangvn2023"><img alt="hailangvn2023" src="https://github.com/hailangvn2023.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/social/tree/17.0/mail_composer_cc_bcc_account">OCA/social</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1 @@
from . import test_mail_cc_bcc

View File

@ -0,0 +1,58 @@
# Copyright 2023 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import date
from odoo.tests import Form
from odoo.addons.mail_composer_cc_bcc.tests.test_mail_cc_bcc import TestMailCcBcc
class TestMailCcBccInvoice(TestMailCcBcc):
def open_invoice_mail_composer_form(self):
# Use form to populate data
# init invoice data
self.test_invoice = test_record = self.test_account_move = self.env[
"account.move"
].create(
{
"invoice_date": date(2024, 3, 2),
"invoice_date_due": date(2024, 3, 10),
"invoice_line_ids": [
(0, 0, {"name": "Line1", "price_unit": 100.0}),
(0, 0, {"name": "Line2", "price_unit": 200.0}),
],
"move_type": "out_invoice",
"name": "invoice test",
"partner_id": self.env.ref("base.res_partner_2").id,
}
)
self.assertTrue(
self.test_invoice,
"Test setup did not succeed. Invoice not found.",
)
self.test_invoice.write({"state": "posted"})
ctx = {
"active_ids": test_record.ids,
"default_model": "account.move",
"default_res_id": test_record.id,
"mail_notify_force_send": True,
}
form = Form(self.env["account.move.send"].with_context(**ctx))
form.mail_body = "<p>Hello</p>"
return form
def test_invoice_mail_cc_bcc(self):
self.set_company()
form = self.open_invoice_mail_composer_form()
form.mail_subject = "Hello"
composer = form.save()
with self.mock_mail_gateway():
composer.action_send_and_print()
message = self.test_invoice.message_ids[0]
self.assertEqual(len(message.mail_ids), 1)
# Only 2 partners (from default_cc/bcc of company) notified
self.assertEqual(len(message.notified_partner_ids), 2)
self.assertEqual(len(message.notification_ids), 2)

View File

@ -0,0 +1 @@
from . import account_move_send

View File

@ -0,0 +1,129 @@
# Copyright 2024 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import Command, api, fields, models, tools
class AccountMoveSend(models.TransientModel):
_inherit = "account.move.send"
partner_cc_ids = fields.Many2many(
"res.partner",
"account_move_send_res_partner_cc_rel",
"wizard_id",
"partner_id",
string="Cc",
compute="_compute_mail_partner_cc_bcc_ids",
store=True,
readonly=False,
)
partner_bcc_ids = fields.Many2many(
"res.partner",
"account_move_send_res_partner_bcc_rel",
"wizard_id",
"partner_id",
string="Bcc",
compute="_compute_mail_partner_cc_bcc_ids",
store=True,
readonly=False,
)
def _get_partner_ids_from_mail(self, move, emails):
partners = self.env["res.partner"].with_company(move.company_id)
for mail_data in tools.email_split(emails):
partners |= partners.find_or_create(mail_data)
return partners
@api.model
def default_get(self, fields_list):
company = self.env.company
res = super().default_get(fields_list)
partner_cc = company.default_partner_cc_ids
if partner_cc:
res["partner_cc_ids"] = [Command.set(partner_cc.ids)]
partner_bcc = company.default_partner_bcc_ids
if partner_bcc:
res["partner_bcc_ids"] = [Command.set(partner_bcc.ids)]
return res
@api.depends("mail_template_id")
def _compute_mail_partner_cc_bcc_ids(self):
for wizard in self:
if wizard.mode == "invoice_single" and wizard.mail_template_id:
wizard.partner_cc_ids = self._get_partner_ids_from_mail(
wizard.move_ids, wizard.mail_template_id.email_cc
)
wizard.partner_bcc_ids = self._get_partner_ids_from_mail(
wizard.move_ids, wizard.mail_template_id.email_bcc
)
else:
wizard.partner_cc_ids = None
wizard.partner_bcc_ids = None
def _get_mail_move_values(self, move, wizard=None):
mail_template_id = (
move.send_and_print_values
and move.send_and_print_values.get("mail_template_id")
)
mail_template = (
wizard
and wizard.mail_template_id
or self.env["mail.template"].browse(mail_template_id)
)
mail_lang = self._get_default_mail_lang(move, mail_template)
return {
"mail_template_id": mail_template,
"mail_lang": mail_lang,
"mail_body": wizard
and wizard.mail_body
or self._get_default_mail_body(move, mail_template, mail_lang),
"mail_subject": wizard
and wizard.mail_subject
or self._get_default_mail_subject(move, mail_template, mail_lang),
"mail_partner_ids": wizard
and wizard.mail_partner_ids
or self._get_default_mail_partner_ids(move, mail_template, mail_lang),
"mail_attachments_widget": wizard
and wizard.mail_attachments_widget
or self._get_default_mail_attachments_widget(move, mail_template),
"partner_cc_ids": wizard
and wizard.partner_cc_ids
or self._get_default_mail_partner_cc_ids(move, mail_template),
"partner_bcc_ids": wizard
and wizard.partner_bcc_ids
or self._get_default_mail_partner_bcc_ids(move, mail_template),
}
# -------------------------------------------------------------------------
# BUSINESS ACTIONS
# -------------------------------------------------------------------------
@api.model
def _send_mail(self, move, mail_template, **kwargs):
"""Send the journal entry passed as parameter by mail."""
partner_ids = kwargs.get("partner_ids", [])
move_with_context = move.with_context(
no_new_invoice=True,
mail_notify_author=self.env.user.partner_id.id in partner_ids,
is_from_composer=True,
partner_cc_ids=self.partner_cc_ids,
partner_bcc_ids=self.partner_bcc_ids,
)
extra_args = {
"email_layout_xmlid": "mail.mail_notification_layout_with_responsible_signature", # noqa: E501
"email_add_signature": not mail_template,
"mail_auto_delete": mail_template.auto_delete,
"mail_server_id": mail_template.mail_server_id.id,
"reply_to_force_new": False,
"message_type": "comment",
}
kwargs.update(extra_args)
new_message = move_with_context.message_post(**kwargs)
# Prevent duplicated attachments linked to the invoice.
new_message.attachment_ids.write(
{
"res_model": new_message._name,
"res_id": new_message.id,
}
)

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="account_move_send_form_inherit" model="ir.ui.view">
<field name="name">account.move.send.form.inherit</field>
<field name="model">account.move.send</field>
<field name="inherit_id" ref="account.account_move_send_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='mail_partner_ids']/.." position="after">
<field name='mode' invisible="1" />
<field
name="partner_cc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
invisible="mode == 'invoice_multi'"
/>
<field
name="partner_bcc_ids"
widget="many2many_tags_email"
context="{'force_email':True, 'show_email':True}"
invisible="mode == 'invoice_multi'"
/>
</xpath>
</field>
</record>
</odoo>