from __future__ import annotations import ssl import xmlrpc.client from dataclasses import dataclass from typing import Any, Dict, List, Optional from app.config import settings class OdooIntegrationError(RuntimeError): pass @dataclass class OdooConfig: base_url: str db: str username: str api_key: str def get_odoo_config() -> OdooConfig: base_url = (settings.odoo_base_url or "").rstrip("/") db = settings.odoo_db or "" username = settings.odoo_username or "" api_key = settings.odoo_api_key or "" if not base_url: raise OdooIntegrationError("ODOO_BASE_URL não está configurado.") if not db: raise OdooIntegrationError("ODOO_DB não está configurado.") if not username: raise OdooIntegrationError("ODOO_USERNAME não está configurado.") if not api_key: raise OdooIntegrationError("ODOO_API_KEY não está configurado.") return OdooConfig(base_url=base_url, db=db, username=username, api_key=api_key) class OdooClient: def __init__(self, config: Optional[OdooConfig] = None): self.config = config or get_odoo_config() self._uid: Optional[int] = None context = ssl._create_unverified_context() self.common = xmlrpc.client.ServerProxy( f"{self.config.base_url}/xmlrpc/2/common", allow_none=True, context=context, ) self.models = xmlrpc.client.ServerProxy( f"{self.config.base_url}/xmlrpc/2/object", allow_none=True, context=context, ) def version(self) -> Dict[str, Any]: try: return dict(self.common.version()) except Exception as exc: raise OdooIntegrationError(f"Falha ao consultar versão Odoo: {exc}") from exc def authenticate(self) -> int: if self._uid: return self._uid try: uid = self.common.authenticate( self.config.db, self.config.username, self.config.api_key, {}, ) except Exception as exc: raise OdooIntegrationError(f"Falha de autenticação Odoo: {exc}") from exc if not uid: raise OdooIntegrationError("Autenticação Odoo falhou. Verifica ODOO_USERNAME e ODOO_API_KEY/password.") self._uid = int(uid) return self._uid def execute_kw( self, model: str, method: str, args: Optional[List[Any]] = None, kwargs: Optional[Dict[str, Any]] = None, ) -> Any: uid = self.authenticate() try: return self.models.execute_kw( self.config.db, uid, self.config.api_key, model, method, args or [], kwargs or {}, ) except Exception as exc: raise OdooIntegrationError(f"Odoo {model}.{method} falhou: {exc}") from exc def search_read( self, model: str, domain: Optional[List[Any]] = None, fields: Optional[List[str]] = None, *, limit: int = 500, offset: int = 0, order: str = "id asc", context: Optional[Dict[str, Any]] = None, ) -> List[Dict[str, Any]]: kwargs: Dict[str, Any] = { "fields": fields or [], "limit": int(limit), "offset": int(offset), "order": order, } if context is not None: kwargs["context"] = context return list(self.execute_kw(model, "search_read", [domain or []], kwargs) or []) def count(self, model: str, domain: Optional[List[Any]] = None) -> int: return int(self.execute_kw(model, "search_count", [domain or []]) or 0)