Introducción
El campo Monetario es uno de los tipos de campo más útiles y más frecuentemente mal utilizados en Odoo. Se parece a un número, se almacena como un flotante, pero no se comporta como ninguno de los dos. Una vez que entiendas qué lo hace diferente, nunca volverás a usar un Float simple para un valor financiero.
Si alguna vez has mirado el total de un pedido de venta, el monto de una factura o el precio de un producto en Odoo, ya has utilizado un campo Monetario. Están en todas partes en el modelo de datos de Odoo, y silenciosamente realizan gran parte del trabajo pesado en torno al formato de moneda, el redondeo y la precisión.
Esta guía es para desarrolladores de Odoo, consultores y usuarios empresariales con mentalidad técnica que desean entender cómo funciona realmente el campo Monetario. Ya sea que estés siguiendo un tutorial de campo de Odoo, construyendo un módulo personalizado o simplemente tratando de entender por qué tus montos se redondean de manera diferente a lo esperado, esta es la referencia que necesitas.
¿Qué es el campo Monetario en Odoo?
El tipo fields.Monetary es uno de los tipos de campo centrales en el marco de Odoo. Está diseñado específicamente para almacenar valores denominados en moneda: precios, cantidades, totales, presupuestos o cualquier número que represente dinero.
Lo que lo distingue de un Float regular es la relación obligatoria con una moneda. Un campo Monetario siempre sabe con qué moneda está trabajando y utiliza esa moneda para determinar cuántos decimales mostrar y cómo redondear los valores almacenados.
Cómo Aparece en la Interfaz
En la interfaz de Odoo, un campo Monetario muestra el valor formateado de acuerdo con la moneda asociada. Si la moneda es Euro, verás algo como € 1,234.50. Si es el Dólar estadounidense, verás $ 1,234.50. El número de decimales sigue la configuración del registro de moneda.
El campo es completamente editable en las vistas de formulario, se muestra de manera clara en las vistas de lista e integra de forma natural en tablas dinámicas e informes financieros. Los usuarios finales no tienen que pensar en el formato en absoluto. Odoo lo maneja de manera transparente.
El Tipo de Datos Subyacente
A nivel de base de datos, un campo Monetario se almacena como un float de doble precisión en PostgreSQL. La información de la moneda no se almacena en la misma columna. Proviene de un registro relacionado res.currency, vinculado a través de un campo Many2one separado en el mismo modelo.
Esta separación de valor y moneda es intencional. Mantiene los campos de la base de datos de Odoo limpios y hace posible cambiar la moneda de un registro independientemente de sus cantidades almacenadas.
Cómo funciona el campo
Entender la mecánica detrás del campo Monetario te ayudará a usarlo correctamente y evitar los problemas de redondeo o visualización que son sorprendentemente comunes en el desarrollo personalizado de Odoo.
El Parámetro currency_field
Cada campo Monetario debe estar emparejado con un campo Many2one que apunte a res.currency. Por defecto, Odoo busca un campo llamado currency_id en el mismo modelo. Puedes anular esto con el parámetro currency_field:
amount = fields.Monetary(string='Cantidad', currency_field='currency_id')
Si el campo de moneda falta o no está configurado en un registro dado, Odoo recurre a la moneda de la empresa. Esto previene errores graves, pero en un entorno multimoneda producirá un formato incorrecto. Siempre declare el campo de moneda de manera explícita.
Redondeo y Precisión
Una de las principales diferencias entre Monetario y Float es cómo funciona el redondeo. El modelo res.currency define el número de decimales y el factor de redondeo para cada moneda. Cuando Odoo lee o muestra un valor Monetario, aplica esas reglas automáticamente.
Esto significa que un valor almacenado como 1.2349999 para un campo EUR se mostrará como 1.23, no 1.235 o 1.23499. Esto es crítico para cálculos fiscales, totales de facturas y cualquier proceso de conciliación financiera. Usar un Float simple en esos contextos eventualmente producirá discrepancias de redondeo que son difíciles de rastrear.
Interacción con el ORM de Odoo
En el ORM de Odoo, leer un campo Monetario siempre devuelve un float de Python. El contexto de la moneda proviene del campo de moneda emparejado en el mismo registro. Al realizar cálculos en Python que involucren campos Monetarios, use el método round() de la moneda para preservar la precisión:
rounded_value = self.currency_id.round(self.amount)
Esto evita errores de acumulación de punto flotante que pueden aparecer en totales de múltiples líneas o cálculos iterativos.
Campos Monetarios en Informes QWeb
En las plantillas de informes QWeb, los campos Monetarios se integran con un widget dedicado que formatea el valor correctamente en PDFs e informes web:
<span t-esc="record.amount"
t-options='{"widget": "monetary", "display_currency": record.currency_id}'/>
Esto asegura el símbolo de moneda correcto y el formato decimal en cada documento generado, independientemente de la moneda en el registro.
Casos de uso empresarial
Los campos monetarios se utilizan en todos los módulos estándar de Odoo. Aquí hay cinco ejemplos concretos que ilustran dónde y por qué son importantes en los flujos de trabajo empresariales reales.
1. Ventas: Precios de Productos y Totales de Pedidos
Campos como price_unit, price_subtotal y amount_total en pedidos de venta y líneas de pedido son todos campos monetarios. Respetan automáticamente la moneda seleccionada en el pedido del cliente, que puede diferir de la moneda operativa de la empresa.
Cuando un representante de ventas crea un pedido en USD para una empresa que opera en EUR, Odoo maneja la visualización, el redondeo y la conversión correctamente porque el campo monetario siempre trabaja en el contexto de la moneda del registro, no en la moneda del sistema.
2. Contabilidad: Montos de Facturas y Líneas de Impuestos
En el módulo de contabilidad, cada columna de monto en una factura es un campo monetario: amount_untaxed, amount_tax, amount_total. La moneda establecida en la factura impulsa el redondeo de todos estos valores.
Este no es un detalle. Un redondeo incorrecto en las líneas de impuestos crea desequilibrios en las entradas del diario que son dolorosos de reconciliar. El redondeo consciente de la moneda del campo monetario es lo que previene esos problemas a nivel de datos.
3. CRM: Ingresos Esperados en Oportunidades
El campo expected_revenue en una oportunidad de CRM es un campo monetario. Los equipos de ventas pueden capturar valores de pipeline en la moneda del prospecto, mientras que los tableros y los informes convierten todo a la moneda de la empresa para el análisis y la previsión del pipeline.
Esta configuración funciona de manera limpia precisamente porque los campos monetarios llevan información de moneda junto con su valor numérico.
4. Compras: Precios de Proveedores y Órdenes de Compra
Las órdenes de compra utilizan campos monetarios para precios unitarios y totales, vinculados a la moneda acordada con el proveedor. Una factura de proveedor en yenes japoneses se maneja de la misma manera que una en euros. El campo monetario se encarga de la precisión y la visualización sin ningún manejo manual por parte del equipo de compras.
5. Campos Personalizados: Presupuesto y Seguimiento de Objetivos
Una solicitud de personalización muy común es agregar un monto de presupuesto, un objetivo de ingresos o un límite de costos a un proyecto, departamento o modelo personalizado. En todos estos casos, un campo Monetario es la opción correcta. Se integra naturalmente con la moneda de la empresa, se muestra correctamente en formularios y listas, y se comporta de manera predecible en informes y exportaciones.
Usar un Float para este tipo de campo es técnicamente posible, pero conduce a inconsistencias de formato y problemas de redondeo tan pronto como surgen escenarios de múltiples monedas.
Creando o personalizando el campo
Hay dos formas principales de agregar un campo Monetario a un modelo de Odoo: a través de Odoo Studio para un enfoque sin código, o a través de un módulo de Python personalizado para un control total.
Usando Odoo Studio
Odoo Studio incluye un tipo Monetario en su panel de creación de campos. Cuando usas los campos de Odoo Studio para agregar un campo Monetario a un modelo, Studio agrega automáticamente un campo currency_id si no existe ya en ese modelo. Esto cubre los casos de uso más comunes sin necesidad de escribir código.
Una cosa de la que hay que tener en cuenta: los campos creados por Studio utilizan el prefijo x_ (por ejemplo, x_studio_budget). Si tu modelo ya tiene un campo currency_id, el nuevo campo Monetario lo utilizará. Si no, Studio creará uno nuevo. Cuando tienes varios campos Monetarios en el mismo modelo con monedas potencialmente diferentes, este campo de moneda compartido merece ser revisado cuidadosamente antes de entrar en producción.
Para escenarios sencillos, Studio es la forma más rápida de crear campos en Odoo y funciona bien para usuarios de negocios que no tienen acceso al desarrollo.
Enfoque Técnico: Campos de Python
En un módulo Odoo personalizado, crear un campo Monetario requiere dos declaraciones: el campo en sí y el campo de moneda asociado. Este es el patrón estándar en el desarrollo de campos de Python en Odoo:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_budget = fields.Monetary(
string='Presupuesto',
currency_field='x_budget_currency_id',
)
x_budget_currency_id = fields.Many2one(
comodel_name='res.currency',
string='Moneda del Presupuesto',
default=lambda self: self.env.company.currency_id,
)
Establecer la moneda de la empresa como la predeterminada es una elección práctica para la mayoría de los campos internos. Evita que el campo de moneda aparezca vacío cuando un usuario abre un registro por primera vez, lo que rompería el formato del campo Monetario.
Campos Monetarios Computados
Los campos monetarios funcionan bien como campos computados en Odoo. Si necesitas totalizar montos de línea o aplicar una fórmula que produzca un resultado monetario, este es el patrón estándar:
x_total_budget = fields.Monetary(
string='Total Budget',
currency_field='currency_id',
compute='_compute_total_budget',
store=True,
)
@api.depends('x_line_ids.x_amount')
def _compute_total_budget(self):
for record in self:
record.x_total_budget = sum(record.x_line_ids.mapped('x_amount'))
El atributo store=True es importante si planeas filtrar, ordenar o agregar en este campo en vistas de lista o informes. Los campos computados no almacenados no se pueden usar en filtros de dominio ORM.
Añadiendo un Campo Monetario a través de la API
Si necesitas crear campos en Odoo programáticamente a través de XML-RPC (por ejemplo, como parte de un script de configuración remota), puedes crear el campo Monetario a través de ir.model.fields:
models.execute_kw(ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_budget',
'field_description': 'Budget',
'model_id': model_id,
'ttype': 'monetary',
'currency_field': 'currency_id',
'state': 'manual',
}]
)
Esto es parte del conjunto de herramientas de personalización más amplio de Odoo disponible a través de la API XML-RPC, que se cubre con más detalle en otros artículos de esta colección de blogs.
Mejores prácticas
El campo Monetario es sencillo de usar una vez que conoces los patrones. Aquí están las prácticas que mantendrán tu implementación limpia y evitarán problemas en el futuro.
1. Nunca Usar Float para Valores Financieros
Esta es la regla más importante en el desarrollo de Odoo cuando se trata de dinero. Si un campo representa un precio, un monto, un total, un presupuesto o cualquier número denotado en moneda, usa fields.Monetary. Un campo Float no tiene conciencia de moneda y no redondeará correctamente entre diferentes monedas. El campo Monetario existe precisamente para resolver este problema.
2. Siempre Declarar el Campo de Moneda de Manera Explícita
No confíes en el recurso de Odoo a currency_id a menos que ese campo realmente exista en tu modelo. Establece explícitamente el parámetro currency_field y siempre declara el correspondiente Many2one a res.currency. Esto hace que la relación sea clara y previene un comportamiento de recurso silencioso en entornos de múltiples divisas.
3. Establecer una Divisa Predeterminada
Para los campos internos que casi siempre usarán la divisa de la empresa, establece un valor predeterminado en el campo de divisa: default=lambda self: self.env.company.currency_id. Esto evita que el campo aparezca en blanco en los nuevos registros, lo que eliminaría el símbolo de la divisa y el formato en la interfaz de usuario.
4. Usa store=True en Campos Monetarios Computados que Planeas Buscar
Si defines un campo Monetario computado y esperas que los usuarios o informes filtren o ordenen por él, establece store=True. Los campos computados no almacenados no pueden aparecer en dominios ORM o vistas basadas en SQL. Esta es una fuente común de confusión al construir paneles personalizados en Odoo.
5. Usa Currency Round() para Cálculos Intermedios
Al escribir código Python que realiza múltiples pasos aritméticos sobre valores Monetarios, aplica self.currency_id.round(value) en cada paso significativo en lugar de solo al final. Los errores de punto flotante se acumulan a lo largo de múltiples operaciones, y aplicar redondeo temprano previene discrepancias en los totales.
6. Sé Intencional en Informes de Múltiples Divisas
Al agregar valores Monetarios a través de registros con diferentes divisas, no simplemente sumes los números en bruto. Convierte todos los valores a una divisa común primero usando res.currency.compute(), o haz que el informe sea específico de la divisa. Mezclar divisas en una suma produce números que son técnicamente correctos a nivel de campo pero financieramente sin sentido.
Errores comunes
Incluso los desarrolladores experimentados de Odoo se encuentran con problemas con los campos Monetarios. Aquí están los errores más comunes y cómo evitarlos.
Trampa 1: Falta de Campo de Divisa
El problema más frecuente al agregar un campo Monetario es olvidar crear el campo Many2one de divisa asociado. Si currency_id no existe en el modelo, Odoo recurrirá silenciosamente a la divisa de la empresa en algunos contextos y generará un error en otros. Siempre crea el campo de divisa junto con el campo Monetario, incluso si nunca esperas que los usuarios lo cambien.
Trampa 2: Dos campos monetarios compartiendo un campo de moneda con diferentes monedas previstas
Si tienes dos campos monetarios en el mismo modelo que están destinados a contener valores en diferentes monedas (por ejemplo, un precio al cliente en EUR y un costo de proveedor en USD), no pueden compartir de manera segura un único campo currency_id. Cada uno necesita su propia referencia de moneda. No hacerlo significa que una configuración de moneda anula ambos campos, lo que producirá datos incorrectos y un comportamiento confuso en la interfaz de usuario.
Trampa 3: Discrepancias de redondeo al agregar entre monedas
Sumar valores de campos monetarios a través de registros con diferentes monedas producirá totales que parecen incorrectos, porque estás sumando montos en EUR a montos en USD como si fueran la misma unidad. Esta es una fuente común de errores de informes en empresas que operan en múltiples monedas. Siempre normaliza a una sola moneda antes de agregar.
Trampa 4: Comparaciones de punto flotante en búsquedas ORM
Buscar un campo monetario con una verificación de igualdad exacta (por ejemplo, buscar registros donde amount = 10.0) puede omitir registros debido a cómo se almacenan los flotantes en la base de datos. Usa >= y <= con una pequeña tolerancia, o aplica redondeo de moneda antes de comparar valores en la lógica de Python. Este es un problema general de odoo orm con campos basados en flotantes, no específico de Monetario, pero es más relevante cuando se trata de dinero.
Trampa 5: Ignorar el redondeo de moneda durante la importación de datos
Al importar registros a través de CSV o XML-RPC que incluyen valores de campos monetarios, los números importados se almacenan tal como se proporcionaron sin redondeo automático. Si tus datos de origen tienen más lugares decimales de los que permite la moneda de destino, el valor almacenado será técnicamente correcto pero se mostrará de manera diferente a lo esperado, y puede causar pequeñas discrepancias en los totales. Aplica redondeo de moneda en tus scripts de importación o pasos de preparación de datos antes de enviar valores a Odoo.
Conclusión
El campo monetario es uno de esos tipos de campo de Odoo que parece simple en la superficie pero tiene un comportamiento muy importante debajo. Su estrecha relación con el registro de moneda es lo que lo hace confiable para los datos financieros: redondeo correcto, formato consistente y visualización consciente de la moneda en toda la interfaz de Odoo y el motor de informes.
Usarlo correctamente, lo que significa siempre emparejarlo con un campo de moneda explícito y nunca sustituir un Float por valores financieros, te salvará de toda una categoría de errores sutiles que son notoriamente difíciles de rastrear en producción. El modelo de datos de odoo está construido en torno a este tipo de campo por una buena razón.
Ya sea que estés siguiendo una guía de desarrollador de odoo, personalizando un módulo estándar o construyendo algo nuevo desde cero, obtener tus campos monetarios correctos es una de las decisiones fundamentales que da forma a la forma en que tu implementación de Odoo maneja el dinero de manera confiable.
¿Necesita ayuda con su implementación de Odoo?
En Dasolo, ayudamos a las empresas a implementar, personalizar y optimizar Odoo en todas las escalas y configuraciones. Ya sea que necesites un modelo de datos limpio, una estrategia de campo personalizada, soporte multimoneda o un despliegue completo de Odoo, nuestro equipo tiene la profundidad técnica y funcional para hacerlo bien.
Si tienes preguntas sobre los campos Monetarios o cualquier otro aspecto de tu implementación de Odoo, estamos encantados de ayudar. Contáctanos y hablemos sobre lo que estás construyendo.