Las operaciones básicas de Odoo con xmlrpc

11 de julio de 2023 por
Gustavo Orrillo
| Sin comentarios aún
 

Como empezar a usar xmlrpc

Vamos a empezar a enseñar como usar xmlrpc con Odoo haciendo un script de Python que imprima "Hola Mundo". Este script de python puede ser invocado desde la línea de comandos (es fundamental para la productividad del programador):

#!/usr/bin/python3
print("Hola Mundo")


La primer línea del script invoca el interprete de Python. La segunda línea del script imprime "Hola Mundo". Nada del otro mundo. Aclaro que estamos trabajando en un entorno Linux, esto puede cambiar para otro entorno como Windows. Seguidamente, para poder ejecutar el script en la línea de comandos tenemos que cambiar los permisos del script para que este sea ejecutable

chmod +x hola_mundo.py


Y luego procedemos a ejecutarlo desde la línea de comandos

./hola_mundo.py


Es fundamental saber como invocar scripts desde la línea de comandos. Nos va a permitir realizar trabajos más complicados (como por ejemplo sincronizar datos de un sistema con bases de datos PostgreSQL con Odoo). .

Como conectarse y autenticarse con Odoo

Lo primero que debemos resolver (un problema que puede llegar a no ser menor por los issues de conectividad que pueden surgir) es la conección con Odoo. Para ello en el script debemos importar la biblioteca xmlrpc y proveer la URL del sistema Odoo con el cual queremos conectarmos. 

Supongamos que tenemos corriendo un Odoo en un server local y deseamos conocer su versión. Para ello tenemos que hacer

#!/usr/bin/python3
from xmlrpc import client
url = 'http://localhost:8069'
common = client.ServerProxy('{}/xmlrpc/2/common'.format(url))
res = common.version()
print(res)

Esto imprimirá la versión del Odoo con el que se conectó en formato json


{'server_version': '15.0', 'server_version_info': [15, 0, 0, 'final', 0, ''], 'server_serie': '15.0', 'protocol_version': 1}

En este ejemplo nos conectamos con un end-point para el cual no se requiere autenticación (la versión Odoo del método dummy de AFIP). Se lo usa para testear la conección con el sistema destino (personalmente prefiero intentar autenticarme). El siguiente paso es autenticarse con Odoo. Para esto debemos proveer la base de datos, el usuario y el password en el script.

dbname = 'demodb'
user = 'admin'
pwd = 'adminpwd'
uid = common.authenticate(dbname, user, pwd, {})
print(uid)

Si la autenticación fue exitosa, Odoo responderá con el ID del usuario autenticado. Si no fue así, devolverá un False.



Usando los métodos básicos del ORM: search, read, create, write, unlink

En un principio debemos saber que el ORM de Odoo implementa al menos cuatro operaciones básicas (no se quejen, con estas operaciones ya se pueden hacer muchas cosas). Búsqueda, creación, escritura y borrado de registros. 

Por ejemplo esto permite exportar datos de Odoo e importar datos en Odoo (supongamos conectandonos a una base de datos PostgreSQL). Existen otras operaciones que se pueden realizar, como por ejemplo search_count que devuelve la cantidad de registros que cumplen con una condición. Pero en mi experiencia estas no son tan comunes cuando se utilizan en scripts (y muchas veces requieren conocer el funcionamiento interno de Odoo). Igual hacia el final de este post vamos a hablar de como invocar métodos de los objetos mediante xmlrpc.

Para realizar operaciones con los registros es necesario utilizar el end-point xmlrpc/2/object. Este end-point es necesario para ejecutar el método remoto execute_kw que utilizaremos para invocar los métodos en Odoo.

models = client.ServerProxy('{}/xmlrpc/2/object'.format(url))

Y cada llamada a execute_kw requiere los siguientes parámetros

  • base de datos
  • el user_id que uno obtiene mediante la función authenticate
  • el password del usuario
  • el nombre del modelo
  • el nombre del método
  • una lista de parámetros que pueden ser ids o condiciones (dependiendo del método)
  • un diccionario opcional de parámetros

Para aprender esto vamos a utilizar un ejemplo en el que vamos a mostrar como se usan los diferentes métodos para la búsqueda, lectura, creación, borrado y escritura. Primero vamos a chequear que en el modelo de paises (res.country) exista un país llamado Odoolandia (lo último que queremos es tener problemas de paises duplicados). Si no existe, crearlo. Luego actualizar su moneda a USD (quieren ahorrarse un banco central), listarlo y por último borrarlo.

Búsqueda de registros


El ORM de Odoo brinda el método search para buscar registros. A este método se le debe proveer como parámetros una lista con los dominios de búsqueda. Por ejemplo, en este caso vamos a buscar


# search
country_ids = models.execute_kw(dbname, uid, pwd, 'res.country', 'search', \
​[[['name', '=', 'Odoolandia']]])
if country_ids:
print('Existe Odoolandia')
else:
print('No existe Odoolandia')

En este ejemplo invocamos mediante execute_kw el método search buscando un pais de nombre Odoolandia. El método search siempre devuelve una lista con los IDs de los registros que cumplen con la condición de búsqueda. Si la operación search no encuentra ningún registro que satisfaga la condición de búsqueda, retorna una lista vacía.

Creación de registros


Un registro se crea invocando el método create. A este método se le debe pasar como parámetro un diccionario de valores con los que se creará el nuevo registro. El método create devuelve del registro creado (o lista de IDs de registros creados). En el caso de error se creará una excepción de Python.


# create
vals = {
'name': 'Odoolandia',
'code': 'OD',
}
return_id = models.execute_kw(dbname,uid, pwd, 'res.country', 'create', [vals])
print('Registro creado, asignado el ID ',return_id) 


También se puede pasar como parámetro una lista de diccionarios con los registros que se van a crear. Esto es bueno porque es más rápido, ya que se ejecuta una sola transacción en PostgreSQL en lugar de múltiples.

Actualización de registros


La actualización de registros se hace con el método 'write'. A este método se le debe pasar como parámetro una lista con dos elementos; el primero es una lista de IDs a actualizar y el segundo es un diccionario de valores que van a actualizar el registro. 

Por ejemplo, si queremos actualizar el país Odoolandia y queremos asignarle la moneda dolar (USD) y el código O1.



# write
# busqueda de moneda USD
currency_usd = models.execute_kw(dbname,uid,pwd,'res.currency','search',\
​[[['name','=','USD']]])
vals = {
'currency_id': currency_usd[0],
'code': 'O1'
}
res = models.execute_kw(dbname,uid,pwd,'res.country','write',[[return_id],vals])
print('Resultado escritura',res)


El método write devuelve dos valores. True (si se actualizó el registro) o False (si hubo error). Noten en el ejemplo que estamos actualizando un campo tipo many2one. Estos campos requieren que se los actualice con el ID del registro relacionado (es por eso que en el ejemplo se realiza la búsqueda del ID de la moneda dolar). 

Lectura de registros

La lectura de registros se hace con el método 'read'. Para ello al método hay que pasarle como parámetros una lista con dos valores: el primero es una lista de IDs de registros a leer y el segundo elemento es opcional con una lista de los campos a listar. 

Por ejemplo, si queremos conocer el campo code, name y currency_id del país que creado anteriormente, debemos hacer

# read
country_data = models.execute_kw(dbname,uid,pwd,'res.country','read',\
​[[return_id],['name','code','currency_id']])
print(country_data)


Esto retorna una lista de diccionarios con los valores leidos.


[{'id': 263, 'name': 'Odoolandia', 'code': 'O1', 'currency_id': [2, 'USD']}]

Noten que los campos many2one devuelven una lista con dos elementos: el primero es el ID del registro relacionado y el segundo es el valor del campo display_name.

Borrado de registros


Supongamos que queremos borrar el registro. Para ello debemos invocar el método unlink pasando como parámetro la lista de registros a borrar.

# unlink
res = models.execute_kw(dbname,uid,pwd,'res.country','unlink',[[return_id]])
print(res)

Este método (al igual que el método write) retorna True si el borrado fue exitoso y False si no se pudo hacerlo.

Código final

Debajo encontrarán el código completo de lo que estuvimos viendo hasta ahora

#!/usr/bin/python3
from xmlrpc import client
url = 'http://localhost:8069'
common = client.ServerProxy('{}/xmlrpc/2/common'.format(url))
res = common.version()
dbname = 'mrputilsv1'
user = 'admin'
pwd = 'admin'
uid = common.authenticate(dbname, user, pwd, {})
print(res)
print(uid)
models = client.ServerProxy('{}/xmlrpc/2/object'.format(url))
# search
country_ids = models.execute_kw(dbname, uid, pwd, 'res.country', 'search', [[['name', '=', 'Odoolandia']]])
if country_ids:
print('Existe Odoolandia')
else:
print('No existe Odoolandia')
# create
vals = {
'name': 'Odoolandia'
}
return_id = models.execute_kw(dbname,uid, pwd, 'res.country', 'create', [vals])
print('Registro creado, asignado el ID ',return_id)
# write
# busqueda de moneda USD
currency_usd = models.execute_kw(dbname,uid,pwd,'res.currency','search',[[['name','=','USD']]])
vals = {
'currency_id': currency_usd[0],
'code': 'O1'
}
res = models.execute_kw(dbname,uid,pwd,'res.country','write',[[return_id],vals])
print('Resultado escritura',res)
# read
country_data = models.execute_kw(dbname,uid,pwd,'res.country','read',[[return_id],['name','code','currency_id']])
print(country_data)
# unlink
res = models.execute_kw(dbname,uid,pwd,'res.country','unlink',[[return_id]])
print(res)


Como usar el contexto e invocando otros métodos

Muchas veces en Odoo debemos invocar los métodos con el contexto (por ejemplo, para la creación de líneas de facturas, o las líneas de los asientos contables). El contexto se debe agregar como ultimo parámetro en la llamada de execute_kw. Por ejemplo,


account_move_id = models.execute_kw(db, uid, pwd, 'account.move',\
​ 'create', [vals], {'context' :{'check_move_validity': False}})


Por último, tambien se pueden invocar métodos en cada uno de los modelos. Por ejemplo para validar una factura (invocando action_post) o confirmando un picking (_action_done). Pero ya no es tan común. Se requiere un conocimiento sobre los internals de Odoo.

Gustavo Orrillo 11 de julio de 2023
Compartir
Archivar
Identificarse dejar un comentario