127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
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)
|