"""Data-adapter base class: routes data lookups across three backends. The fusion_accounting_ai sub-module's tools (e.g. get_unreconciled_bank_lines) must work in any of three install profiles: 1. FUSION mode — a fusion native sub-module (e.g. fusion_accounting_bank_rec) is installed; route to its model. 2. ENTERPRISE mode — Odoo Enterprise (e.g. account_accountant) is installed; route to Enterprise APIs. 3. COMMUNITY mode — neither; fall back to a pure Odoo Community search/read. Subclasses implement the three backend methods and define which fusion model and which Enterprise module they probe. """ import enum import logging from typing import Any _logger = logging.getLogger(__name__) class AdapterMode(enum.Enum): FUSION = "fusion" ENTERPRISE = "enterprise" COMMUNITY = "community" class DataAdapter: """Base class. Subclasses set FUSION_MODEL and ENTERPRISE_MODULE class attrs and implement _via_fusion(...), _via_enterprise(...), _via_community(...).""" # Override in subclasses. FUSION_MODEL: str = "" ENTERPRISE_MODULE: str = "" def __init__(self, env): self.env = env def _select_mode( self, fusion_native_model: str | None = None, enterprise_module: str | None = None, ) -> AdapterMode: """Pick FUSION if the model is loaded, else ENTERPRISE if the module is installed, else COMMUNITY.""" fusion_model = fusion_native_model or self.FUSION_MODEL ent_module = enterprise_module or self.ENTERPRISE_MODULE if fusion_model and fusion_model in self.env: return AdapterMode.FUSION if ent_module: installed = self.env['ir.module.module'].sudo().search_count([ ('name', '=', ent_module), ('state', '=', 'installed'), ]) if installed: return AdapterMode.ENTERPRISE return AdapterMode.COMMUNITY def _dispatch(self, method_name: str, *args, **kwargs) -> Any: """Look up _via_ on self and call it. E.g. method_name='list_unreconciled', mode=FUSION calls self.list_unreconciled_via_fusion(*args, **kwargs). """ mode = self._select_mode() attr = f"{method_name}_via_{mode.value}" impl = getattr(self, attr, None) if impl is None: _logger.warning( "DataAdapter %s has no implementation for %s in mode %s; " "returning empty result", type(self).__name__, method_name, mode.value, ) return [] return impl(*args, **kwargs)