From 4fb7ef38ed6fdfde3a3f6720147bde62dce66c2f Mon Sep 17 00:00:00 2001 From: Kristof Bernaert Date: Mon, 26 Jan 2026 03:56:26 +0100 Subject: [PATCH] Fix allow_mail_archived_partner causing system notifications to send emails --- .../models/account_move_send.py | 32 +++-- .../models/mail_thread.py | 36 ++++-- .../models/res_partner.py | 111 ++++++++++++------ 3 files changed, 124 insertions(+), 55 deletions(-) diff --git a/allow_mail_archived_partner/models/account_move_send.py b/allow_mail_archived_partner/models/account_move_send.py index 50969ad..0aee72a 100755 --- a/allow_mail_archived_partner/models/account_move_send.py +++ b/allow_mail_archived_partner/models/account_move_send.py @@ -18,9 +18,8 @@ class AccountInvoiceSend(models.TransientModel): @api.model def default_get(self, fields): - """ - Simply add archived partners to the wizard. - """ + _logger.error("🔥 ACCOUNT.INVOICE.SEND default_get") + res = super().default_get(fields) active_ids = self.env.context.get('active_ids', []) @@ -32,13 +31,32 @@ class AccountInvoiceSend(models.TransientModel): partners = moves.mapped('partner_id').filtered(lambda p: p.email) if partners: res['partner_ids'] = [(6, 0, partners.ids)] + _logger.error(f"🔥 Set partner_ids: {res['partner_ids']}") return res def action_send_and_print(self): - """ - Just pass context - let parent handle everything. - """ + _logger.error("🔥🔥🔥 ACCOUNT.INVOICE.SEND action_send_and_print CALLED 🔥🔥🔥") + _logger.error(f"🔥 Wizard ID: {self.id}") + _logger.error(f"🔥 Partner IDs: {self.partner_ids.ids}") + _logger.error(f"🔥 Current context: {dict(self.env.context)}") + + # CRITICAL: Pass ALL required context flags + return super( + AccountInvoiceSend, + self.with_context( + active_test=False, # For res.partner searches + include_archived_partners=True, # For mail_thread.py + mail_notify_force=True, # For mail_thread.py + force_email=True, # Already present + mark_invoice_as_sent=True, # Already present + ) + ).action_send_and_print() + + def _get_composer_values(self, res_ids, template): + _logger.error("🔥 ACCOUNT.INVOICE.SEND _get_composer_values") + + # Also pass context to composer return super( AccountInvoiceSend, self.with_context( @@ -48,4 +66,4 @@ class AccountInvoiceSend(models.TransientModel): force_email=True, mark_invoice_as_sent=True, ) - ).action_send_and_print() \ No newline at end of file + )._get_composer_values(res_ids, template) \ No newline at end of file diff --git a/allow_mail_archived_partner/models/mail_thread.py b/allow_mail_archived_partner/models/mail_thread.py index 99377eb..88cade4 100755 --- a/allow_mail_archived_partner/models/mail_thread.py +++ b/allow_mail_archived_partner/models/mail_thread.py @@ -23,7 +23,7 @@ class MailThread(models.AbstractModel): def _notify_get_recipients(self, message, msg_vals, **kwargs): """ - Allow archived partners ONLY for explicit manual sends. + Allow archived partners for ANY manual email send. """ _logger.error("🔥🔥🔥🔥🔥 MailThread._notify_get_recipients CALLED 🔥🔥🔥🔥🔥") _logger.error(f"🔥 Model: {self._name}") @@ -31,25 +31,30 @@ class MailThread(models.AbstractModel): _logger.error(f"🔥 Message subject: {getattr(message, 'subject', 'No subject')}") _logger.error(f"🔥 Full context: {dict(self.env.context)}") - # 1) Check if this is a MANUAL send (invoice wizard or compose message) + # Check MULTIPLE indicators of manual send is_manual_send = ( self.env.context.get("mail_notify_force") or self.env.context.get("include_archived_partners") or self.env.context.get("force_email") or # From invoice wizard - self.env.context.get("mark_invoice_as_sent") # From invoice wizard + self.env.context.get("mark_invoice_as_sent") or # From invoice wizard + # Additional checks for email sends + (self._name == "account.move" and + self.env.context.get("default_composition_mode") == "comment") or + (msg_vals and msg_vals.get("message_type") == "email") ) _logger.error(f"🔥 Is manual send? {is_manual_send}") - _logger.error(f"🔥 Context flags - mail_notify_force: {self.env.context.get('mail_notify_force')}") - _logger.error(f"🔥 Context flags - include_archived_partners: {self.env.context.get('include_archived_partners')}") - _logger.error(f"🔥 Context flags - force_email: {self.env.context.get('force_email')}") - _logger.error(f"🔥 Context flags - mark_invoice_as_sent: {self.env.context.get('mark_invoice_as_sent')}") + _logger.error(f"🔥 All context flags: mail_notify_force={self.env.context.get('mail_notify_force')}, " + f"include_archived={self.env.context.get('include_archived_partners')}, " + f"force_email={self.env.context.get('force_email')}, " + f"mark_invoice_as_sent={self.env.context.get('mark_invoice_as_sent')}") - # 2) For MANUAL sends, allow archived partners with full context + # For MANUAL sends, allow archived partners with full context if is_manual_send: _logger.error("🔥🔥🔥 ✓ MANUAL SEND DETECTED - Allowing archived partners 🔥🔥🔥") + _logger.error(f"🔥 Calling super() with context: active_test=False, include_archived_partners=True, mail_notify_force=True") - # Get recipients with context that allows archived partners + # Use context that allows archived partners recipients = super( MailThread, self.with_context( @@ -67,12 +72,14 @@ class MailThread(models.AbstractModel): return recipients - # 3) BLOCK system notifications completely + # BLOCK system notifications completely is_system_notification = ( getattr(message, "message_type", None) == "notification" or (getattr(message, "author_id", False) and message.author_id == self.env.ref("base.partner_root", raise_if_not_found=False)) or - (msg_vals and msg_vals.get("message_type") == "notification") + (msg_vals and msg_vals.get("message_type") == "notification") or + getattr(message, "subtype_id", False) and + getattr(message.subtype_id, "internal", False) ) if is_system_notification: @@ -87,10 +94,13 @@ class MailThread(models.AbstractModel): return recipients - # 4) Default behavior for non-manual, non-system sends + # Default behavior for non-manual, non-system sends _logger.error("🔥🔥🔥 ○ DEFAULT BEHAVIOR - No special handling 🔥🔥🔥") recipients = super()._notify_get_recipients(message, msg_vals, **kwargs) _logger.error(f"🔥 Default recipients found: {len(recipients)}") + for i, recipient in enumerate(recipients): + _logger.error(f"🔥 Default recipient {i}: partner_id={recipient.get('partner_id')}, " + f"notif={recipient.get('notif')}") return recipients def _message_post(self, **kwargs): @@ -104,7 +114,6 @@ class MailThread(models.AbstractModel): return super()._message_post(**kwargs) - # Add message_post method too (called by chatter) def message_post(self, **kwargs): """ Log when messages are posted via chatter. @@ -113,5 +122,6 @@ class MailThread(models.AbstractModel): _logger.error(f"🔥 Model: {self._name}") _logger.error(f"🔥 Kwargs keys: {kwargs.keys()}") _logger.error(f"🔥 Context: {dict(self.env.context)}") + _logger.error(f"🔥 Partner IDs in kwargs: {kwargs.get('partner_ids', [])}") return super().message_post(**kwargs) \ No newline at end of file diff --git a/allow_mail_archived_partner/models/res_partner.py b/allow_mail_archived_partner/models/res_partner.py index 39de529..52c2e05 100755 --- a/allow_mail_archived_partner/models/res_partner.py +++ b/allow_mail_archived_partner/models/res_partner.py @@ -14,13 +14,70 @@ class ResPartner(models.Model): """ Allow archived partners when context permits. """ - _logger.info("=== ResPartner._search ===") - _logger.info(f"Search args: {args}") - _logger.info(f"Context include_archived_partners: {self.env.context.get('include_archived_partners')}") - _logger.info(f"Context mail_notify_force: {self.env.context.get('mail_notify_force')}") - _logger.info(f"Context force_email: {self.env.context.get('force_email')}") - _logger.info(f"Context mark_invoice_as_sent: {self.env.context.get('mark_invoice_as_sent')}") - _logger.info(f"Full context active_test: {self.env.context.get('active_test')}") + _logger.error("🔥 RES.PARTNER._search CALLED 🔥") + _logger.error(f"🔥 Search args: {args}") + _logger.error(f"🔥 Context include_archived_partners: {self.env.context.get('include_archived_partners')}") + _logger.error(f"🔥 Context mail_notify_force: {self.env.context.get('mail_notify_force')}") + _logger.error(f"🔥 Context force_email: {self.env.context.get('force_email')}") + _logger.error(f"🔥 Context mark_invoice_as_sent: {self.env.context.get('mark_invoice_as_sent')}") + _logger.error(f"🔥 Context active_test: {self.env.context.get('active_test')}") + + # Check if we should include archived partners + include_archived = ( + self.env.context.get("include_archived_partners") or + self.env.context.get("mail_notify_force") or + self.env.context.get("force_email") or + self.env.context.get("mark_invoice_as_sent") + ) + + _logger.error(f"🔥 Should include archived? {include_archived}") + + if include_archived: + _logger.error("🔥🔥🔥 RES.PARTNER: CONTEXT FLAGS FOUND - Allowing archived partners 🔥🔥🔥") + + # Remove both active and partner_share filters + filtered_args = [] + for arg in args: + if isinstance(arg, (list, tuple)) and len(arg) == 3: + # Skip active filters + if arg[0] == "active": + _logger.error(f"🔥 Removing active filter: {arg}") + continue + # Skip partner_share filter for manual sends + if arg[0] == "partner_share" and arg[1] == "=" and arg[2] is True: + _logger.error(f"🔥 Removing partner_share filter: {arg}") + continue + # Also remove active in ('=', 'in', 'not in') with any value + if arg[0] == "active" and arg[1] in ("=", "in", "not in"): + _logger.error(f"🔥 Removing active filter (any value): {arg}") + continue + + filtered_args.append(arg) + + args = filtered_args + self = self.with_context(active_test=False) + _logger.error(f"🔥 Search args after cleanup: {args}") + + result = super()._search(args, offset, limit, order, count, access_rights_uid) + + if not count: + result_ids = list(result) + _logger.error(f"🔥 Search returned {len(result_ids)} results") + if result_ids: + _logger.error(f"🔥 First 3 result IDs: {result_ids[:3]}") + # Get partner details for debugging + partners = self.browse(result_ids[:3]) + for partner in partners: + _logger.error(f"🔥 Partner {partner.id}: {partner.name}, active={partner.active}, email={partner.email}") + + return result + + @api.model + def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None): + """ + Also override name_search to include archived partners when context allows. + """ + _logger.error(f"🔥 RES.PARTNER._name_search CALLED: name='{name}', args={args}") # Check if we should include archived partners include_archived = ( @@ -31,33 +88,17 @@ class ResPartner(models.Model): ) if include_archived: - _logger.info("✓ CONTEXT FLAGS FOUND - Allowing archived partners") - - # Remove both active and partner_share filters - filtered_args = [] - for arg in args: - if isinstance(arg, (list, tuple)) and len(arg) == 3: - # Skip active filters - if arg[0] == "active": - _logger.info(f" Removing active filter: {arg}") - continue - # Skip partner_share filter for manual sends - if arg[0] == "partner_share" and arg[1] == "=" and arg[2] is True: - _logger.info(f" Removing partner_share filter: {arg}") - continue - - filtered_args.append(arg) - - args = filtered_args + _logger.error("🔥 _name_search: Allowing archived partners") + # Remove active filters from args + if args is None: + args = [] + args = [ + arg for arg in args + if not (isinstance(arg, (list, tuple)) and len(arg) == 3 and arg[0] == "active") + ] self = self.with_context(active_test=False) - _logger.info(f"Search args after cleanup: {args}") - result = super()._search(args, offset, limit, order, count, access_rights_uid) - - if not count: - result_ids = list(result) - _logger.info(f"Search returned {len(result_ids)} results") - if result_ids: - _logger.info(f"First 5 result IDs: {result_ids[:5]}") - - return result \ No newline at end of file + return super()._name_search( + name=name, args=args, operator=operator, + limit=limit, name_get_uid=name_get_uid + ) \ No newline at end of file