En mi (no tan) larga historia con Odoo siempre me gustó la flexibilidad del sistema para conectarse desde fuera. Este es uno de los mejores argumentos a la hora de elegirlo por sobre otros ERP, Odoo tiene múltiples protocolos de conexión, tiene sus API y tiene la posibilidad de crear API a medida. Sin embargo, aquellos que se enfrentaron a construir una especie de API o WebHook puede que se encuentren con un problema que parece un chiste: Odoo no permite crear un contacto (res.partner) desde un Controller. Y no es un tema de permisos, no importa que pongamos funciones sudo() en cada línea, nunca creará el Contacto, nos dará una bad query en PostgreSQL y quedaremos un buen rato mirando una terminal sin respuesta.
El Error en cuestión
Tiene múltiples formatos pero casi siempre es algo parecido a esto:
2023-08-29 18:02:17,835 4181363 ERROR odoo odoo.sql_db: bad query:
SELECT substr(p.res_id, 13)::integer, r.id
FROM ir_property p
LEFT JOIN stock_location r ON substr(p.value_reference, 16)https://::integer=r.id
WHERE p.fields_id=5870
AND (p.company_id=false OR p.company_id IS NULL)
AND (p.res_id IN ('res.partner,5857') OR p.res_id IS NULL)
ORDER BY p.company_id NULLS FIRST
ERROR: el operador no existe: integer = boolean
LÍNEA 6: AND (p.company_id=false OR p.company_id ...
Traducido: estamos guardando un res.partner que debería ser un número entero (el ID de la base) y se guarda un null; es decir, no se está creando. Lo malo de este error es que no nos indica que el problema viene de mucho antes, en el controller propiamente dicho, que necesita tener definido el usuario creador del res.partner. Así que hay que volver al controller.
¿Solución?
Bueno, vamos a tener que modificar el controller, el primer error es que el auth sea none en lugar de public:
@http.route('/mi/ruta', type="json", auth="public", cors='*')
Al no tener auth (es decir, que sea none) no existe una sesión de usuario para crear el res.partner, cosa que odoo necesita obligatoriamente, por eso le diremos que es público.
Puede que aun con esto siga sin funcionar, en ese caso necesitaremos habilitarle una sesión al controller:
user = https://request.session.authenticate(db=company.odoo_db, login=company.odoo_login, password=company.odoo_password)
session_info = request.env['ir.http'].session_info()
Y con esto sin dudas quedará solucionado. Lo ideal es que la sesión se cree con otro controller donde los datos de conexión se pasen una vez, en caso de no poderlo hacer (por ejemplo, con un WebHook de terceros) podemos hacer como en este ejemplo, y traernos los tres datos base de unos campos cifrados en el sistema.