"""Cross-conversation company/opportunity linking helpers. v4928.1.5.91: B2B customers often use separate inboxes for buying and finance (e.g. geral@empresa.pt orders, financeiro@empresa.pt pays). A new Chatwoot conversation must therefore not automatically create a new opportunity when it references an existing document/process. Rules implemented here: - exact document references (ORC/FA/FT formats) are strong automatic evidence; - a business domain is company evidence only, never enough when multiple open opportunities exist; - public/ISP domains are ignored for identity linking. """ from __future__ import annotations import json import re from typing import Any, Dict, Iterable, List, Optional from sqlalchemy import text from app.db import engine PUBLIC_EMAIL_DOMAINS = { "gmail.com", "googlemail.com", "hotmail.com", "hotmail.pt", "outlook.com", "outlook.pt", "live.com", "msn.com", "yahoo.com", "yahoo.pt", "icloud.com", "me.com", "mac.com", "sapo.pt", "mail.telepac.pt", "netcabo.pt", "clix.pt", "iol.pt", "vodafone.pt", "mail.com", "proton.me", "protonmail.com", } FINANCE_DEPARTMENT_PREFIXES = { "financeiro", "financas", "finanças", "contabilidade", "accounts", "accounting", "pagamentos", "payments", "billing", "facturacao", "faturacao", "faturação", } BUYING_DEPARTMENT_PREFIXES = { "compras", "purchasing", "procurement", "encomendas", "orders", "geral", "info", } # Explicit stored formats: ORC.ORC2026.193, ORC2026.193, FA.FA2026.131, FT.FT2026.1 _EXPLICIT_DOC_RE = re.compile( r"\b(?PORC|FA|FT|FR|NC|ND)[.\-/ ]?(?P=prefix)?\s*(?P20\d{2})[.\-/ ]?0*(?P\d{1,6})\b", re.IGNORECASE, ) # Human formats common in email subjects: Orçamento 2026/193, Fatura 2026/131. _HUMAN_DOC_RE = re.compile( r"\b(?P