fix: update Odoo client for v19 compatibility

- Use persistent httpx client to maintain session cookies
- Update endpoint path to /web/dataset/call_kw/{model}/{method}
- Handle auth response as dict (uid extraction)
- Remove kanban_state field (doesn't exist in Odoo 19)
- Add close() method for graceful shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 10:10:36 +00:00
parent 9b5d9a7b3c
commit 5e3d8d45de
2 changed files with 28 additions and 14 deletions

View File

@@ -73,6 +73,7 @@ async def lifespan(app: FastAPI):
task = asyncio.create_task(refresh_loop()) task = asyncio.create_task(refresh_loop())
yield yield
task.cancel() task.cancel()
await odoo_client.close()
app = FastAPI(title="TV Dashboard API", lifespan=lifespan) app = FastAPI(title="TV Dashboard API", lifespan=lifespan)

View File

@@ -10,6 +10,16 @@ class OdooClient:
self.username = username self.username = username
self.password = password self.password = password
self.uid: int | None = None self.uid: int | None = None
self._client: httpx.AsyncClient | None = None
async def _get_client(self) -> httpx.AsyncClient:
if self._client is None or self._client.is_closed:
self._client = httpx.AsyncClient(timeout=30.0)
return self._client
async def close(self) -> None:
if self._client and not self._client.is_closed:
await self._client.aclose()
async def _jsonrpc(self, endpoint: str, params: dict[str, Any]) -> Any: async def _jsonrpc(self, endpoint: str, params: dict[str, Any]) -> Any:
payload = { payload = {
@@ -18,11 +28,10 @@ class OdooClient:
"params": params, "params": params,
"id": 1, "id": 1,
} }
async with httpx.AsyncClient() as client: client = await self._get_client()
response = await client.post( response = await client.post(
f"{self.url}{endpoint}", f"{self.url}{endpoint}",
json=payload, json=payload,
timeout=30.0,
) )
response.raise_for_status() response.raise_for_status()
result = response.json() result = response.json()
@@ -31,11 +40,15 @@ class OdooClient:
return result.get("result") return result.get("result")
async def authenticate(self) -> int: async def authenticate(self) -> int:
self.uid = await self._jsonrpc("/web/session/authenticate", { result = await self._jsonrpc("/web/session/authenticate", {
"db": self.database, "db": self.database,
"login": self.username, "login": self.username,
"password": self.password, "password": self.password,
}) })
if isinstance(result, dict):
self.uid = result.get("uid")
else:
self.uid = result
return self.uid return self.uid
async def search_read( async def search_read(
@@ -46,7 +59,7 @@ class OdooClient:
limit: int = 0, limit: int = 0,
order: str = "", order: str = "",
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
return await self._jsonrpc("/web/dataset/call_kw", { return await self._jsonrpc(f"/web/dataset/call_kw/{model}/search_read", {
"model": model, "model": model,
"method": "search_read", "method": "search_read",
"args": [domain or []], "args": [domain or []],
@@ -73,7 +86,7 @@ class OdooClient:
domain=domain, domain=domain,
fields=[ fields=[
"name", "project_id", "stage_id", "user_ids", "name", "project_id", "stage_id", "user_ids",
"priority", "date_deadline", "kanban_state", "priority", "date_deadline",
], ],
) )