Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[17.0][FIX] mail_composer_cc_bcc: fix duplicate key value error "unique_mail_message_id_res_partner_id_if_set" #1494

Open
wants to merge 4 commits into
base: 17.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions mail_composer_cc_bcc/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ confusing for a lot of end users.
This module allows to properly separate To:, Cc:, and Bcc: fields in the
Mail Composer.

From Odoo 17.0, this module sends one mail per recipient and keeps same
all headers (To, Cc, Bcc) in all emails

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
Expand Down Expand Up @@ -87,12 +88,6 @@ corresponding mail composer's fields.
.. |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
===========

Expand Down
1 change: 1 addition & 0 deletions mail_composer_cc_bcc/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
"views/mail_message_views.xml",
"views/mail_template_views.xml",
"wizards/mail_compose_message_view.xml",
"wizards/mail_template_preview_view.xml",
],
}
7 changes: 7 additions & 0 deletions mail_composer_cc_bcc/models/ir_mail_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ def _prepare_email_message(self, message, smtp_session):
"""
Define smtp_to based on context instead of To+Cc+Bcc
"""
x_odoo_bcc_value = next(
(value for key, value in message._headers if key == "X-Odoo-Bcc"), None
)
# Add Bcc field inside message to pass validation
if x_odoo_bcc_value:
message["Bcc"] = x_odoo_bcc_value

smtp_from, smtp_to_list, message = super()._prepare_email_message(
message, smtp_session
)
Expand Down
10 changes: 7 additions & 3 deletions mail_composer_cc_bcc/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _prepare_outgoing_list(self, recipients_follower_status=None):

# Collect recipients (RCPT TO) and update all emails
# with the same To, Cc headers (to be shown by email client as users expect)
recipients = []
recipients = set()
for m in res:
rcpt_to = None
if m["email_to"]:
Expand All @@ -64,7 +64,7 @@ def _prepare_outgoing_list(self, recipients_follower_status=None):
rcpt_to = extract_rfc2822_addresses(m["email_cc"][0])[0]

if rcpt_to:
recipients.append(rcpt_to)
recipients.add(rcpt_to)

m.update(
{
Expand All @@ -74,5 +74,9 @@ def _prepare_outgoing_list(self, recipients_follower_status=None):
}
)

self.env.context = {**self.env.context, "recipients": recipients}
self.env.context = {**self.env.context, "recipients": list(recipients)}

if len(res) > len(recipients):
res.pop()

return res
190 changes: 1 addition & 189 deletions mail_composer_cc_bcc/models/mail_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


import itertools

from odoo import fields, models, tools
from odoo import fields, models


class MailTemplate(models.Model):
Expand All @@ -13,189 +11,3 @@ class MailTemplate(models.Model):
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
20 changes: 17 additions & 3 deletions mail_composer_cc_bcc/models/mail_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ def _notify_by_email_get_base_mail_values(self, message, additional_values=None)
message, additional_values=additional_values
)
context = self.env.context
skip_adding_cc_bcc = context.get("skip_adding_cc_bcc", False)
if skip_adding_cc_bcc:
return res

partners_cc = context.get("partner_cc_ids", None)
if partners_cc:
Expand All @@ -55,7 +58,8 @@ def _notify_get_recipients(self, message, msg_vals, **kwargs):
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:
skip_adding_cc_bcc = context.get("skip_adding_cc_bcc", False)
if not is_from_composer or skip_adding_cc_bcc:
return rdata
for pdata in rdata:
pdata["type"] = "customer"
Expand All @@ -69,9 +73,13 @@ def _notify_get_recipients(self, message, msg_vals, **kwargs):
recipients_cc_bcc = MailFollowers._get_recipient_data(
None, message_type, subtype_id, partners_cc_bcc.ids
)
partners_already_marked_as_recipient = [r.get("id", False) for r in rdata]
for _, value in recipients_cc_bcc.items():
for _, data in value.items():
if not data.get("id"):
if (
not data.get("id")
or data.get("id") in partners_already_marked_as_recipient
):
continue
if not data.get(
"notif"
Expand All @@ -96,7 +104,8 @@ def _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:
skip_adding_cc_bcc = self.env.context.get("skip_adding_cc_bcc", False)
if not is_from_composer or skip_adding_cc_bcc:
return res
ids = []
customer_data = None
Expand All @@ -112,3 +121,8 @@ def _notify_get_recipients_classify(
else:
customer_data["recipients"] += ids
return [customer_data]

def _notify_thread(self, message, msg_vals=False, **kwargs):
if message.message_type == "notification":
self = self.with_context(skip_adding_cc_bcc=True)
return super()._notify_thread(message, msg_vals, **kwargs)
4 changes: 2 additions & 2 deletions mail_composer_cc_bcc/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ confusing for a lot of end users.
This module allows to properly separate To:, Cc:, and Bcc: fields in the
Mail Composer.

From Odoo 17.0, this module sends one mail per recipient and keeps same all headers (To, Cc, Bcc) in all emails

## 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
Expand Down
2 changes: 0 additions & 2 deletions mail_composer_cc_bcc/readme/ROADMAP.md

This file was deleted.

Loading
Loading