#51 docs(opencode): skill tryton-rpc-probe y agente de proyecto
Documenta como hacer requests RPC de prueba contra Tryton con
sabatron-tryton-rpc-client:
- .opencode/skills/tryton-rpc-probe/SKILL.md: comando base con docker compose
dev (--no-deps), credenciales desde .env, como revelar estados HTTP
(401/403/429) que la libreria enmascara, convencion de agrupacion de campos
con punto ('template.' -> 'categories.') y sondas de ejemplo
- .opencode/agents/tryton-rpc-probe.md: subagente que obliga a cargar el skill
- AGENTS.md: referencia al skill para todos los agents del proyecto
This commit is contained in:
23
.opencode/agents/tryton-rpc-probe.md
Normal file
23
.opencode/agents/tryton-rpc-probe.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
description: Ejecuta requests RPC de prueba contra Tryton usando sabatron-tryton-rpc-client, siguiendo el skill tryton-rpc-probe. Úsalo para probes aislados (model.product.*, model.product.category.*, fields_get, credenciales staging).
|
||||||
|
mode: subagent
|
||||||
|
permission:
|
||||||
|
bash:
|
||||||
|
"docker compose *": allow
|
||||||
|
"*": ask
|
||||||
|
---
|
||||||
|
|
||||||
|
Eres un agente especializado en ejecutar requests RPC de prueba (probes)
|
||||||
|
contra Tryton dentro de este proyecto.
|
||||||
|
|
||||||
|
Antes de ejecutar cualquier probe, **carga el skill `tryton-rpc-probe`** y sigue
|
||||||
|
exactamente sus instrucciones:
|
||||||
|
- Usa el paquete `sabatron-tryton-rpc-client` (nunca curl).
|
||||||
|
- Lee las credenciales `TRYTON_*` desde `.env` y pásalas con `-e` al contenedor.
|
||||||
|
- Usa `docker compose -f docker-compose.dev.yml run --rm --no-deps ...` para no
|
||||||
|
levantar dependencias.
|
||||||
|
- Ante `HTTP 401` (credenciales), `403` (denegado) o `429` (rate-limit) usa la
|
||||||
|
técnica de revelar el estado HTTP real descripta en el skill y reporta el
|
||||||
|
código al usuario en lugar de asumir el motivo.
|
||||||
|
- Reporta los resultados crudos (campos, agrupaciones `field.`, ids) tal cual
|
||||||
|
los devuelve Tryton, y su implicación para la implementación.
|
||||||
154
.opencode/skills/tryton-rpc-probe/SKILL.md
Normal file
154
.opencode/skills/tryton-rpc-probe/SKILL.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
name: tryton-rpc-probe
|
||||||
|
description: Use cuando se necesite hacer requests RPC de prueba contra Tryton (probes) con el paquete sabatron-tryton-rpc-client. Dispara ante tryton, sabatron, probe rpc, model.product.search/read, model.product.category.read, fields_get, credenciales Tryton staging o verificaciones empíricas de respuestas RPC. NO usar curl: solo el paquete de python.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Probes RPC a Tryton con sabatron-tryton-rpc-client
|
||||||
|
|
||||||
|
Guía para ejecutar requests RPC de prueba (probes) contra Tryton dentro de este
|
||||||
|
proyecto, usando el paquete `sabatron-tryton-rpc-client` y el contenedor de
|
||||||
|
desarrollo. Sirve para verificar campos, modelos, credenciales y respuestas
|
||||||
|
antes de planear o implementar cambios de integración (TDD: confirmar empíricamente
|
||||||
|
antes de asumir).
|
||||||
|
|
||||||
|
## Reglas obligatorias
|
||||||
|
|
||||||
|
- **Nunca usar curl.** Solo el paquete python `sabatron-tryton-rpc-client`
|
||||||
|
(el RPC de Tryton enmascara/muestra estados HTTP distintos que confunden a curl).
|
||||||
|
- **No hardcodear credenciales**: leer los valores `TRYTON_HOST`,
|
||||||
|
`TRYTON_DATABASE`, `TRYTON_USERNAME`, `TRYTON_PASSWORD` de `.env` (raíz del
|
||||||
|
proyecto) y pasarlos al contenedor con `-e`. No imprimir la contraseña (a lo
|
||||||
|
sumo imprimir el nombre de la base de datos).
|
||||||
|
- **Respetar rate-limit**: ante `HTTP 429` esperar (p. ej. ~120s) antes de
|
||||||
|
reintentar; no hacer ráfagas de logins fallidos.
|
||||||
|
- Verificar primero el login/credenciales con una sonda mínima antes de asumir
|
||||||
|
que falla por campos o datos.
|
||||||
|
|
||||||
|
## Comando base (plantilla)
|
||||||
|
|
||||||
|
Todos los probes se ejecutan con `docker compose` dev sin levantar dependencias
|
||||||
|
(`--no-deps`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.dev.yml run --rm --no-deps \
|
||||||
|
-e TRYTON_HOST=<host de .env> \
|
||||||
|
-e TRYTON_DATABASE=<db de .env> \
|
||||||
|
-e TRYTON_USERNAME=<usuario de .env> \
|
||||||
|
-e TRYTON_PASSWORD=<password de .env> \
|
||||||
|
django python -c '
|
||||||
|
import os
|
||||||
|
from sabatron_tryton_rpc_client.client import Client
|
||||||
|
c = Client(hostname=os.environ["TRYTON_HOST"], database=os.environ["TRYTON_DATABASE"], username=os.environ["TRYTON_USERNAME"], password=os.environ["TRYTON_PASSWORD"])
|
||||||
|
c.connect()
|
||||||
|
print("CONNECTED OK", os.environ["TRYTON_DATABASE"])
|
||||||
|
ctx = {"company": 1}
|
||||||
|
print(c.call("model.product.product.search", [[["salable", "=", True]], 0, 5, [["rec_name", "ASC"], ["id", None]], ctx]))
|
||||||
|
'
|
||||||
|
```
|
||||||
|
|
||||||
|
Patrón de la librería:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sabatron_tryton_rpc_client.client import Client
|
||||||
|
c = Client(hostname=..., database=..., username=..., password=...)
|
||||||
|
c.connect() # login
|
||||||
|
resultado = c.call("modelo.metodo", (args..., contexto)) # contexto {"company": 1}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ver estados HTTP reales (401/403/429 enmascarados)
|
||||||
|
|
||||||
|
La librería **enmascara** los errores HTTP del login como
|
||||||
|
`UnboundLocalError: cannot access local variable 'response'...` (bug en
|
||||||
|
`jsonrpc.py:__request`). Para ver el código real, monkeypatchear el transporte
|
||||||
|
XML-RPC y capturar `ProtocolError`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import xmlrpc.client
|
||||||
|
from sabatron_tryton_rpc_client import jsonrpc
|
||||||
|
orig = xmlrpc.client.Transport.request
|
||||||
|
def wrapped(self, host, handler, body, verbose=False):
|
||||||
|
try:
|
||||||
|
return orig(self, host, handler, body, verbose=verbose)
|
||||||
|
except xmlrpc.client.ProtocolError as e:
|
||||||
|
print("HTTP_STATUS", e.errcode, repr(e.errmsg))
|
||||||
|
raise
|
||||||
|
jsonrpc.Transport.request = wrapped
|
||||||
|
# luego Client(...).connect() / ServerProxy(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
Guía rápida de códigos vistos en el proyecto:
|
||||||
|
|
||||||
|
- `HTTP 200` + fault JSON (p. ej. `FATAL: database "..." does not exist`): la DB
|
||||||
|
no existe o el endpoint responde con error de aplicacion.
|
||||||
|
- `HTTP 401`: credenciales rechazadas (usuario/password) en esa base de datos.
|
||||||
|
- `HTTP 403`: ruta/método denegado (p. ej. `common.db.list`).
|
||||||
|
- `HTTP 429`: rate-limit, esperar antes de reintentar.
|
||||||
|
|
||||||
|
## Convención de campos RPC de Tryton (agrupación con punto)
|
||||||
|
|
||||||
|
Los campos con punto (ej. `template.categories.name`) NO llegan como una clave
|
||||||
|
plana: Tryton los agrupa bajo la clave del modelo padre con puntos.
|
||||||
|
|
||||||
|
En `model.product.product.read`, con los fields
|
||||||
|
`["id", "name", "template.categories", "template.categories.name"]`, la
|
||||||
|
respuesta llega así:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 488,
|
||||||
|
"name": "Cloruro de magnesio",
|
||||||
|
"template.": {
|
||||||
|
"id": 1248,
|
||||||
|
"categories": [6],
|
||||||
|
"categories.": [{"id": 6, "name": "Alimentos Transformados Y Granos"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Combinaciones verificadas en staging (Tryton 8):
|
||||||
|
|
||||||
|
| fields solicitados | claves resultantes |
|
||||||
|
|---|---|
|
||||||
|
| `template.categories` | `categories: [ids]` (sin `categories.`) |
|
||||||
|
| `template.categories.id` | `categories.: [{"id": ...}]` |
|
||||||
|
| `template.categories.name` | `categories.: [{"id", "name"}]` (id viene incluido) |
|
||||||
|
| ambos `.id` + `.name` | igual que solo `.name` |
|
||||||
|
|
||||||
|
Conclusión operativa: pedir `template.categories.name` basta para traer ids y
|
||||||
|
nombres; `template.categories.id` es redundante. No hay `active` en
|
||||||
|
`product.category` (fields_get lo confirma) → activar/desactivar categorías es
|
||||||
|
solo local.
|
||||||
|
|
||||||
|
## Ejemplos de sondas útiles
|
||||||
|
|
||||||
|
Login + datos de productos con categorias anidadas:
|
||||||
|
|
||||||
|
```python
|
||||||
|
ctx = {"company": 1}
|
||||||
|
pids = c.call("model.product.product.search", [[["salable", "=", True]], 0, 1000, [["rec_name", "ASC"], ["id", None]], ctx])
|
||||||
|
detalles = c.call("model.product.product.read", (pids, ["id", "name", "default_uom.id", "default_uom.rec_name", "list_price", "template.categories", "template.categories.name"], ctx))
|
||||||
|
```
|
||||||
|
|
||||||
|
Listar los campos reales de un modelo (evita asumir campos):
|
||||||
|
|
||||||
|
```python
|
||||||
|
campos = c.call("model.product.category.fields_get", ([], ctx))
|
||||||
|
print(sorted(campos))
|
||||||
|
```
|
||||||
|
|
||||||
|
Listar bases de datos (suele estar bloqueado con 403):
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sabatron_tryton_rpc_client.jsonrpc import ServerProxy
|
||||||
|
p = ServerProxy(hostname, 8000, "")
|
||||||
|
print(p.common.db.list())
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integración con tests
|
||||||
|
|
||||||
|
- Los tests de integración NO llaman Tryton real: mockean
|
||||||
|
`sabatron_tryton_rpc_client.client.Client.call` y `.connect`
|
||||||
|
(ver `don_confiao/tests/test_products_from_tryton.py`,
|
||||||
|
`test_categories_from_tryton.py`).
|
||||||
|
- Los probes de esta skill son solo para verificación empírica/planeación
|
||||||
|
contra staging, no para la suite de tests.
|
||||||
18
AGENTS.md
18
AGENTS.md
@@ -468,3 +468,21 @@ Antes de ir a producción, verificar:
|
|||||||
|
|
||||||
## Integraciones
|
## Integraciones
|
||||||
- **Tryton ERP**: Integración mediante sabatron-tryton-rpc-client para sincronización de clientes, productos y ventas
|
- **Tryton ERP**: Integración mediante sabatron-tryton-rpc-client para sincronización de clientes, productos y ventas
|
||||||
|
|
||||||
|
## Probes RPC a Tryton
|
||||||
|
|
||||||
|
Cuando se necesiten requests RPC de prueba contra Tryton (verificación empírica
|
||||||
|
de campos/modelos/credenciales, planeación de integraciones, etc.) **cargar el
|
||||||
|
skill `tryton-rpc-probe`** (`.opencode/skills/tryton-rpc-probe/SKILL.md`) y
|
||||||
|
seguir sus instrucciones:
|
||||||
|
|
||||||
|
- Usar `sabatron-tryton-rpc-client` en el contenedor dev (`run --rm --no-deps`),
|
||||||
|
nunca curl.
|
||||||
|
- Credenciales desde `.env` (TRYTON_HOST/DATABASE/USERNAME/PASSWORD), sin
|
||||||
|
hardcodear ni imprimir secretos.
|
||||||
|
- Los errores HTTP (401/403/429) llegan enmascarados como `UnboundLocalError`:
|
||||||
|
revelar el código real monkeypatcheando `xmlrpc.client.Transport.request`.
|
||||||
|
- Los campos con punto de Tryton se agrupan en `field.` (ej. `template.` →
|
||||||
|
`categories.`); pedir `template.categories.name` ya trae id+nombre.
|
||||||
|
- Para trabajo de integración/código se usan mocks de `Client.call` (no Tryton
|
||||||
|
real) en `don_confiao/tests/test_*_from_tryton.py`.
|
||||||
|
|||||||
Reference in New Issue
Block a user