Fix allow_mail_archived_partner causing system notifications to send emails
This commit is contained in:
Executable
+1
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
'name': 'Allow Emails to Archived Contacts',
|
||||
'version': '1.0',
|
||||
'category': 'Tools',
|
||||
'summary': 'Allow sending emails to archived contacts for Sales and Invoices',
|
||||
'depends': ['base', 'mail', 'sale', 'account'],
|
||||
'data': [],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# models/__init__.py
|
||||
from . import mail_thread
|
||||
from . import res_partner
|
||||
from . import mail_template
|
||||
# from . import mail_compose_message
|
||||
from . import account_move_send
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class AccountMoveSend(models.TransientModel):
|
||||
_inherit = "account.move.send"
|
||||
|
||||
@api.model
|
||||
def _get_default_mail_partner_ids(self, move, mail_template, mail_lang):
|
||||
"""
|
||||
The invoice 'Send by Email' wizard uses this to compute partner_ids (To:).
|
||||
We must disable active_test here to include archived partners.
|
||||
"""
|
||||
wiz = self.with_context(active_test=False, include_archived_partners=True)
|
||||
return super(AccountMoveSend, wiz)._get_default_mail_partner_ids(move, mail_template, mail_lang)
|
||||
@@ -0,0 +1,48 @@
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MailComposeMessage(models.TransientModel):
|
||||
_inherit = "mail.compose.message"
|
||||
|
||||
def _get_default_recipients(self):
|
||||
"""
|
||||
This is the ONLY place that fills the 'To' field in Odoo 16.
|
||||
We explicitly allow archived partners here for user-initiated sends.
|
||||
"""
|
||||
recipients = super()._get_default_recipients()
|
||||
|
||||
model = self.env.context.get("active_model")
|
||||
active_ids = self.env.context.get("active_ids") or []
|
||||
|
||||
if model == "account.move" and active_ids:
|
||||
moves = self.env["account.move"].with_context(
|
||||
active_test=False
|
||||
).browse(active_ids)
|
||||
|
||||
partners = moves.mapped("partner_id").filtered(
|
||||
lambda p: p.email
|
||||
)
|
||||
|
||||
if partners:
|
||||
recipients["partner_ids"] = [(6, 0, partners.ids)]
|
||||
recipients["email_to"] = ", ".join(partners.mapped("email"))
|
||||
|
||||
return recipients
|
||||
|
||||
def _prepare_mail_values(self, res_ids):
|
||||
"""
|
||||
Explicit user send → allow archived partners internally.
|
||||
"""
|
||||
model = self.env.context.get("active_model") or self.model
|
||||
|
||||
if model in ["sale.order", "account.move"]:
|
||||
return super(
|
||||
MailComposeMessage,
|
||||
self.with_context(
|
||||
active_test=False,
|
||||
include_archived_partners=True,
|
||||
mail_notify_force=True,
|
||||
)
|
||||
)._prepare_mail_values(res_ids)
|
||||
|
||||
return super()._prepare_mail_values(res_ids)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MailTemplate(models.Model):
|
||||
_inherit = "mail.template"
|
||||
|
||||
def generate_email(self, res_ids, fields=None):
|
||||
"""
|
||||
Only allow archived partners when *explicitly* sending through a flow
|
||||
that sets:
|
||||
context['mail_notify_force'] = True
|
||||
|
||||
This prevents system notifications from inheriting your relaxed rules.
|
||||
"""
|
||||
if self.model in ["sale.order", "account.move"] and self.env.context.get("mail_notify_force"):
|
||||
return super(
|
||||
MailTemplate,
|
||||
self.with_context(
|
||||
active_test=False,
|
||||
include_archived_partners=True
|
||||
)
|
||||
).generate_email(res_ids, fields)
|
||||
|
||||
return super().generate_email(res_ids, fields)
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MailThread(models.AbstractModel):
|
||||
_inherit = "mail.thread"
|
||||
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
"""
|
||||
Do NOT force notifications, do NOT set mail_notification=True,
|
||||
do NOT globally disable active_test here.
|
||||
|
||||
The parent method decides whether a message should notify.
|
||||
"""
|
||||
return super()._notify_thread(message, msg_vals=msg_vals, **kwargs)
|
||||
|
||||
def _notify_get_recipients(self, message, msg_vals, **kwargs):
|
||||
"""
|
||||
CRITICAL RULES:
|
||||
- Never turn system notifications into outgoing emails.
|
||||
- Never fabricate recipients when Odoo returns none.
|
||||
- Never force notif='email'.
|
||||
|
||||
If archived partners should be allowed, that must be opt-in
|
||||
via context['include_archived_partners'] and only for explicit user sends,
|
||||
not tracking/system messages.
|
||||
"""
|
||||
# 1) Never email system notifications (tracking, auto messages, etc.)
|
||||
# These are the ones that caused "Re: Draft Bill ..." and odoobot@example.com.
|
||||
if getattr(message, "message_type", None) == "notification":
|
||||
return super()._notify_get_recipients(message, msg_vals, **kwargs)
|
||||
|
||||
# 2) Never auto-notify supplier bills. Vendor bills created from email/EDI
|
||||
# must not trigger replies to vendors via chatter notifications.
|
||||
# (If you DO want it for customer invoices, leave those alone.)
|
||||
if self._name == "account.move":
|
||||
# self can be multi; if any are supplier bills, just don't customize at all
|
||||
# (safer than partial overriding recipient lists)
|
||||
if any(m.move_type == "in_invoice" for m in self):
|
||||
return super()._notify_get_recipients(message, msg_vals, **kwargs)
|
||||
|
||||
# 3) Default behavior first
|
||||
recipients = super()._notify_get_recipients(message, msg_vals, **kwargs)
|
||||
|
||||
# 4) Do NOT fabricate recipients if empty.
|
||||
# If you need archived partner support, handle it in explicit sending flows
|
||||
# (compose + template), not here.
|
||||
return recipients
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
@api.model
|
||||
def _search(
|
||||
self,
|
||||
args,
|
||||
offset=0,
|
||||
limit=None,
|
||||
order=None,
|
||||
count=False,
|
||||
access_rights_uid=None
|
||||
):
|
||||
"""
|
||||
Only include archived partners when the caller explicitly opts in via:
|
||||
context['include_archived_partners'] = True
|
||||
|
||||
Never key off fragile string matches like 'mail'/'notify'/'message'.
|
||||
"""
|
||||
if self.env.context.get("include_archived_partners"):
|
||||
# Remove explicit active=True filters only (do not touch other domains)
|
||||
args = [
|
||||
arg for arg in args
|
||||
if not (
|
||||
isinstance(arg, (list, tuple))
|
||||
and len(arg) == 3
|
||||
and arg[0] == "active"
|
||||
and arg[1] == "="
|
||||
and arg[2] is True
|
||||
)
|
||||
]
|
||||
# Also ensure active_test is off so ORM won't auto-filter archived partners
|
||||
self = self.with_context(active_test=False)
|
||||
|
||||
return super()._search(
|
||||
args,
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
order=order,
|
||||
count=count,
|
||||
access_rights_uid=access_rights_uid
|
||||
)
|
||||
Reference in New Issue
Block a user