Skip to content

OD212 monetary-without-currency

A Monetary field's currency field is not a field of its model.

Category Severity Series Needs the index
Correctness Error all yes

What it reports

A Monetary field whose currency_field, given by keyword or as its second argument, names no field of the model, or that gives none on a model with neither currency_id nor x_currency_id. Every definition of the field in the module and its dependencies counts, so a currency_field set where the field was first defined is kept by a redefinition that does not set it. A module that Odoo always installs with base, such as web, counts as in depends. The field is reported once per module, at its last definition there.

From 15.0 a related Monetary is held to the same check: it does not take the currency field of the field it relates to.

Why it matters

A Monetary amount is rounded and displayed with the currency of the record, which Odoo reads from the currency field. When it sets up the model, Odoo asserts that the field is one of the model's, and the assertion stops the registry from loading: the module cannot be installed, and a database that has it does not start.

A field defined only by a module outside the dependencies, a currency_field that is not a string literal or is unknown, and a definition that unpacks **kwargs are not reported. An attribute takes its value from the last definitions that set it, and is unknown when those disagree, as two definitions in different files of one module may: files load in the order of the imports, which is not indexed. A definition of another type drops what the ones before it set, and when its order against them is unknown, the field is not reported.

A field an ir.model.fields record adds, such as x_currency_id, counts as OD304 describes, but only from a record of a dependency: Odoo asserts the currency field ahead of the module's own data files, whatever their order. Define the field in Python, or move the record to a dependency.

The rule does not run for a module when the module or one of its dependencies depends on a module that is not indexed. A model is skipped when the class that introduces it, with _name or as a class Odoo names, is in a module outside the dependencies, or when a class of it, or of a model it inherits or delegates to, has a plain Python base class or an _inherit element that is not a literal, a parent whose fields the scan cannot see.

Example

models/hr_expense.py
class HrExpenseAdvance(models.Model):
    _name = "hr.expense.advance"
    _description = "Expense advance"

    amount = fields.Monetary()

The model has no currency_id, so the amount has no currency.

models/hr_expense.py
class HrExpenseAdvance(models.Model):
    _name = "hr.expense.advance"
    _description = "Expense advance"

    currency_id = fields.Many2one("res.currency")
    amount = fields.Monetary()

Suppressing

amount = fields.Monetary()  # odooscan: ignore[OD212]

See suppressing a finding.