Введение
Поле Monetary — это особый тип в Odoo, который внешне похож на число, под капотом хранится как float, но по сути управляет денежными значениями иначе, чем обычный числовой тип. Поняв его логику, вы почти никогда не захотите заменять его на простой Float при работе с деньгами.
Если вы когда‑либо смотрели на итог заказа, сумму счета или цену товара в Odoo, вы уже взаимодействовали с Monetary. Эти поля встречаются повсеместно в модели данных и автоматически берут на себя форматирование валюты, правила округления и точность представления.
Материал рассчитан на разработчиков Odoo, консультантов и технически подкованных пользователей, которые хотят понять, как именно ведёт себя Monetary. Если вы делаете кастомный модуль, настраиваете отчёты или боретесь с неожиданными округлениями — здесь вы найдёте практическое объяснение.
Что такое поле Monetary в Odoo
Тип fields.Monetary — одна из базовых конструкций в фреймворке Odoo. Он предназначен для хранения значений, выраженных в деньгах: цены, суммы, итоги, бюджеты и любые показатели, привязанные к конкретной валюте.
Главное отличие от обычного Float — обязательная связка с записью валюты. Поле Monetary всегда «знает», в какой валюте хранится значение, и опирается на параметры этой валюты при отображении количества знаков и при округлении.
Как поле выглядит в интерфейсе
В интерфейсе Odoo Monetary форматируется согласно связанной валюте: для евро вы увидите, например, € 1 234,50, для доллара — $ 1,234.50. Количество десятичных знаков и символ валюты берутся из настроек записи валюты.
Поле редактируемо в формах, аккуратно отображается в списках и корректно работает в сводных отчётах и pivot‑таблицах. Пользователю не нужно заботиться о форматировании — Odoo делает это автоматически.
Что реально хранится в базе
На уровне базы данных Monetary хранится как double precision (float) в PostgreSQL. Информация о валюте не пишется в ту же колонку: она хранится в отдельной связке Many2one, ссылающейся на res.currency, расположенной в той же модели.
Такое разделение значения и валюты задумано специально: оно делает поля чище и даёт возможность менять валюту записи независимо от числового значения.
Как это поле работает
Понимание этой механики поможет избежать неожиданных проблем с округлением и отображением, которые часто возникают при кастомной разработке в Odoo.
Параметр currency_field
У каждого поля Monetary должен быть указанный Many2one на res.currency. По умолчанию Odoo ищет поле с именем currency_id в той же модели, но вы можете явно назвать другой атрибут через параметр currency_field:
amount = fields.Monetary(string='Amount', currency_field='currency_id')
Если поле валюты отсутствует или не заполнено, Odoo подставит валюту компании. Это предотвращает ошибки, но в сценариях с несколькими валютами приведёт к неверному форматированию. Поэтому всегда указывайте поле валюты явно.
Округление и точность
Ключевой момент: правила округления применяются не самим полем, а записью валюты (res.currency). Именно в ней задаются число десятичных знаков и фактор округления, и Odoo применяет эти правила при чтении и выводе Monetary‑значений.
Следствие: число вроде 1.2349999 при валюте EUR отобразится как 1.23 — не 1.235. Это критично для расчётов налогов, итогов счётов и сверок. Если использовать простой Float в таких сценариях, со временем вы получите расхождения, которые сложно отследить.
Взаимодействие с ORM
В ORM Odoo при чтении Monetary возвращается Python float. Контекст валюты приходит из связанного поля на той же записи. При вычислениях в Python для сохранения точности применяйте метод округления валюты:
rounded_value = self.currency_id.round(self.amount)
Это снижает риск накопления ошибок с плавающей точкой при суммировании строк или итеративных вычислениях.
Отчёты QWeb и форматирование
В QWeb‑шаблонах Monetary интегрируется с виджетом, который правильно форматирует значения в PDF и на веб‑страницах:
<span t-esc="record.amount"
t-options='{"widget": "monetary", "display_currency": record.currency_id}'/>
Так вы гарантируете корректный символ валюты и десятичное представление в любых сгенерированных документах, независимо от валюты записи.
Примеры бизнес‑сценариев
Где в Odoo Monetary действительно важен — пять практических областей
1. Продажи: цены и итоги заказов
Поля вроде price_unit, price_subtotal и amount_total на заказах и позициях — Monetary. Они подстраиваются под валюту клиента, которая может отличаться от валюты компании.
Когда менеджер создаёт заказ в USD для компании, работающей в EUR, Odoo корректно отображает и округляет суммы, потому что Monetary учитывает валюту конкретной записи, а не глобальную систему.
2. Бухгалтерия: суммы счетов и налоговые строки
В счетах все денежные колонки — Monetary: amount_untaxed, amount_tax, amount_total. Рounding управляется валютой счёта.
Это не мелочь: ошибки в округлении налоговых строк приводят к дисбалансу в проводках и головной боли при сверке. Монетарное поле нивелирует такие проблемы ещё на уровне данных.
3. CRM: ожидаемая выручка
Поле expected_revenue в возможностях CRM — Monetary. Команды могут хранить прогнозы в валюте клиента, а отчёты агрегируют их в валюту компании для анализа конвейера и прогнозирования.
Такое поведение возможно благодаря тому, что Monetary хранит значение вместе с указанием валюты.
4. Закупки: цены поставщиков и заказы
В закупках Monetary используется для единичных цен и итогов, привязанных к валюте поставщика. Счет от японского поставщика в иенах обрабатывается так же, как счёт в евро — точность и отображение берёт на себя поле.
5. Кастомные поля: бюджеты и цели
Если вам нужно добавить бюджет, целевой доход или лимит затрат в проект или отдел, правильный выбор — поля Monetary. Они гармонично интегрируются с интерфейсом, отчётностью и экспортом.
Технически можно хранить такие значения в Float, но в мультивалютной среде это приведёт к проблемам форматирования и округления.
Создание и настройка поля
Две привычные стратегии добавления Monetary в модель: Odoo Studio для без‑кода и Python‑модуль для полного контроля.
Добавление через Odoo Studio
В Studio есть тип Monetary в панели создания полей. При добавлении Studio автоматически создаст currency_id, если его нет в модели. Это быстро и удобно для большинства задач без написания кода.
Помните, что Studio генерирует поля с префиксом x_ (например, x_studio_budget). Если модель уже содержит currency_id, новое поле будет его использовать; если нет, Studio создаст своё. Когда на одной модели несколько Monetary с разными валютами — обязательно проверьте логику общих/отдельных полей валюты перед запуском.
Для простых сценариев Studio — самый быстрый путь, особенно для бизнес‑пользователей без доступа к разработке.
Технический способ: объявление в Python
В кастомном модуле поле Monetary объявляется в паре с полем валюты — это стандартный шаблон для разработки в Odoo:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_budget = fields.Monetary(
string='Budget',
currency_field='x_budget_currency_id',
)
x_budget_currency_id = fields.Many2one(
comodel_name='res.currency',
string='Budget Currency',
default=lambda self: self.env.company.currency_id,
)
Установка валюты компании в качестве значения по умолчанию — практичный выбор для внутренних полей. Это предотвращает пустую валюту на новых записях и обеспечивает корректное форматирование по умолчанию.
Вычисляемые Monetary‑поля
Monetary хорошо работают как вычисляемые поля. Если нужно суммировать строки или применять формулу, используйте стандартный шаблон:
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'))
Атрибут store=True важен, если планируется фильтрация, сортировка или агрегирование по полю. Неструктурированные вычисляемые поля нельзя использовать в ORM‑фильтрах.
Создание поля через API
Если поля нужно создавать программно через XML‑RPC (например, в скрипте удалённой конфигурации), Monetary можно добавить через модель 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',
}]
)
Это часть инструментов кастомизации Odoo через XML‑RPC; детали юзкейсов и примеры обычно рассматриваются в посвящённых статьях.
Рекомендации по использованию
Простые правила помогут избежать проблем в будущем. Ниже — набор практик, которые стоит соблюдать.
1. Никогда не используйте Float для денежных значений
Это базовое правило разработки в Odoo: деньги — это не просто число. Для цен, сумм, итогов, бюджетов и любых значений, выраженных в валюте, используйте fields.Monetary. Float не умеет учитывать валюту и правила округления, поэтому рано или поздно приведёт к ошибкам.
2. Всегда явно указывайте поле валюты
Не полагайтесь на скрытую подстановку currency_id. Явно задавайте параметр currency_field и определяйте соответствующий Many2one на res.currency. Это делает связь прозрачной и предотвращает молчаливую подмену валюты в мультивалютной среде.
3. Задавайте валюту по умолчанию
Для внутренних полей, которые в большинстве случаев будут в валюте компании, установите default: default=lambda self: self.env.company.currency_id. Это исключит пустые валютные поля у новых записей и обеспечит корректный внешний вид значений.
4. Ставьте store=True для вычисляемых полей, по которым будут фильтры
Если вычисляемое Monetary‑поле планируется использовать в фильтрации или сортировке, нужно указать store=True. Нехранимые вычисляемые поля нельзя применять в доменах ORM или SQL‑видах — это частая причина недоразумений при построении дашбордов.
5. Для промежуточных вычислений используйте currency.round()
При выполнении нескольких арифметических шагов на денежных значениях вызывайте self.currency_id.round(value) на ключевых шагах, а не только в конце. Это уменьшит накопление ошибок с плавающей точкой и даст согласованные итоги.
6. Будьте внимательны в мультивалютных отчётах
Агрегируя Monetary‑значения из разных валют, не просто суммируйте числа. Сначала приведите всё к одной валюте через res.currency.compute() или делайте отчёт в конкретной валюте. Смешивание валют делает итоговые суммы бессмысленными.
Типичные ошибки и где их искать
Даже опытные разработчики совершают ошибки с Monetary. Ниже — самые частые промахи и способы их предотвращения.
Ошибка 1: отсутствие поля валюты
Частая проблема — забыть создать связанный Many2one на валюту. Если currency_id отсутствует, в некоторых местах Odoo тихо подставит валюту компании, а в других вызовет ошибку. Всегда объявляйте поле валюты вместе с Monetary, даже если пользователи не будут его менять.
Ошибка 2: две Monetary‑колонки на одной валюте, а на деле нужны разные
Если на одной модели есть два денежных поля, которые должны храниться в разных валютах (например, цена клиента в EUR и себестоимость поставщика в USD), они не должны делить общий currency_id. Каждое поле должно иметь свою ссылку на валюту — иначе одна настройка перезапишет обе и данные станут некорректны.
Ошибка 3: ошибки округления при агрегировании across валют
Суммирование Monetary‑полей из разных валют даёт «неправильные» итоги, потому что складываются разные единицы. Это частая причина ошибок в отчётах. Нормализуйте значения в одну валюту перед суммой.
Ошибка 4: сравнения float в поисках ORM
Поиск с точным сравнением денежных значений (например, amount = 10.0) может пропустить записи из‑за особенностей хранения float в БД. Используйте диапазоны (>=/<=) с допустимой погрешностью или применяйте округление валюты перед сравнением в Python. Это общий нюанс работы с float‑полями, но критичен для денег.
Ошибка 5: игнорирование правил округления при импорте
При импорте через CSV или XML‑RPC Monetary‑значения сохраняются как переданы, без автоматического округления. Если исходные данные содержат более знаков, чем позволяет валюта, результат будет храниться «точно», но отображаться иначе и в суммах появятся небольшие расхождения. Применяйте округление валюты в скриптах импорта.
Заключение
Поле Monetary выглядит просто, но под собой скрывает ключевую бизнес‑логику. Именно связь со справочником валют обеспечивает корректное округление, единое форматирование и предсказуемое поведение во всем интерфейсе и отчётности Odoo.
Правильное использование — всегда вместе с явным полем валюты и ни в коем случае не замена Float — избавит вас от множества трудноуловимых багов в продакшне. Модель данных Odoo строилась с учётом этого типа, и это не случайность.
Будь вы разработчиком, настраивающим модуль, или консультантом, проектирующим архитектуру данных, верные решения по Monetary — фундамент того, насколько корректно ваша система будет работать с финансами.
Нужна помощь с внедрением Odoo?
В компании Dasolo мы помогаем предприятиям настраивать, кастомизировать и оптимизировать Odoo вне зависимости от масштаба и сложности. Нужны продуманная модель данных, стратегия для денежных полей, поддержка мультивалютности или полный запуск Odoo — у нас есть экспертиза для этого.
Если остались вопросы по Monetary или другим аспектам внедрения Odoo, мы готовы помочь. Свяжитесь с нами и обсудим вашу задачу.