class TierRouter: """Plain Python router — not an Odoo model. Handles channel routing for outgoing messages and inbox fallback for incoming. Mailbox assignment is now manual-only (via wizard). """ def __init__(self, env): self.env = env def route_outgoing(self, message, partner, source_model=None): """Assign channel tag to an outgoing message based on routing rules. Mailboxes are no longer auto-assigned — user does it manually. """ channel = self._resolve_channel(partner) if channel: message.channel_tag_ids = [(4, channel.id)] def route_incoming(self, message): """Route an incoming message. If it's a reply — inherit mailboxes from parent. Otherwise — place in standard Odoo inbox. """ parent = message.parent_id if parent and parent.mailbox_tag_ids: for tag in parent.mailbox_tag_ids: self.env['tier.mailbox.tag'].create({ 'mailbox_id': tag.mailbox_id.id, 'message_id': message.id, }) else: self._fallback_inbox(message) def _fallback_inbox(self, message): """Place message in the standard Odoo inbox via mail.notification.""" partner = self.env.user.partner_id if not partner: return existing = self.env['mail.notification'].sudo().search([ ('mail_message_id', '=', message.id), ('res_partner_id', '=', partner.id), ], limit=1) if not existing: self.env['mail.notification'].sudo().create({ 'mail_message_id': message.id, 'res_partner_id': partner.id, 'notification_type': 'inbox', 'is_read': False, }) def _resolve_channel(self, partner): """Return the best channel tag for partner from routing rules.""" if not partner: return None rule = self.env['tier.routing.channel'].search( [('partner_id', '=', partner.id)], order='priority desc', limit=1, ) if not rule: return None preferred = rule.preferred_channel_tag_id if preferred and preferred.active: return preferred for tag in rule.channel_tag_ids.filtered('active'): return tag return None