# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Plating product family. # # Sub 6 — Recipient resolver. # # Every outbound-notification path now routes its recipient lookup # through `_fp_resolve_notification_recipients`. The helper consults # child contacts (and optionally a delivery-location partner's child # contacts) for per-stream flags, falling back to the company's own # `email` when no contact opts in. This preserves the pre-Sub-6 # behaviour exactly for customers who haven't configured any flags. from odoo import models FP_STREAMS = ('certs', 'qc', 'quotes_so', 'invoices') class ResPartner(models.Model): _inherit = 'res.partner' def _fp_resolve_notification_recipients(self, stream, delivery_location=None): """Return a list of email addresses that should receive a given notification stream for this customer. Args: stream: one of 'certs', 'qc', 'quotes_so', 'invoices'. Any other value returns the bare fallback list. delivery_location: optional res.partner (typically with type= 'delivery') whose own child contacts are consulted first, at the same priority as the company-level contacts. Fallback: if no contact at either level carries a matching flag (or the global flag), the result is the company partner's own email. This makes the resolver drop-in safe — no customer ever silently stops receiving notifications after Sub 6 ships. """ self.ensure_one() recipients = [] # Gather candidate contact recordsets: location-scoped first, then # company-scoped. Duplicates are dropped by the final dedup pass. candidate_sets = [] if delivery_location and delivery_location != self: candidate_sets.append(delivery_location.child_ids) candidate_sets.append(self.child_ids) flag_name = f'x_fc_receives_{stream}' if stream in FP_STREAMS else None for contacts in candidate_sets: for contact in contacts: if not contact.email: continue is_global = getattr(contact, 'x_fc_is_global_contact', False) matches_stream = ( flag_name is not None and getattr(contact, flag_name, False) ) if is_global or matches_stream: recipients.append(contact.email) if not recipients: # Nobody opted in via contacts — fall back to the company # email (and the location's email, if distinct). if delivery_location and delivery_location != self and delivery_location.email: recipients.append(delivery_location.email) if self.email: recipients.append(self.email) # Case-insensitive dedup, preserve first-seen order. seen = set() unique = [] for email in recipients: key = email.strip().lower() if key and key not in seen: seen.add(key) unique.append(email) return unique