Skip to content

Odoo glossary

ir.cron

Also called Scheduled actions, Odoo cron job, scheduled action

Definition

ir.cron is the Odoo model behind Scheduled Actions: each record tells the server's cron workers to run a piece of Python code against a model at a fixed interval, such as every hour or every day. Modules usually ship them as XML data records that call a model method.

Overdue rental reminders, nightly currency rate updates, clearing out old draft records: anything that should happen without a user clicking a button usually ends up as a scheduled action. In developer mode they’re listed as Scheduled Actions in the Technical settings menu, and each one is an ir.cron record.

What a cron record is made of

An ir.cron is built on top of a server action. It uses delegation inheritance on ir.actions.server, which is why the cron itself has model_id, state and code fields even though they live on the server action. The fields you set most often:

  • name, shown in the list and in log lines.
  • model_id, the model whose method runs. In the code, model is an empty recordset of it.
  • state set to code, and code holding the call, such as model._cron_flag_overdue().
  • interval_number and interval_type, where the type is one of minutes, hours, days, weeks or months.
  • nextcall, the next planned run. It defaults to now, so a new cron runs soon after install.
  • user_id, the user the job runs as, and priority, where 0 is highest.

Defining one in a module

The method goes on the model:

from odoo import api, fields, models


class EquipmentRental(models.Model):
    _name = "equipment.rental"
    _description = "Equipment Rental"
    # state, return_date and is_overdue fields defined here

    @api.model
    def _cron_flag_overdue(self):
        overdue = self.search([
            ("state", "=", "out"),
            ("return_date", "<", fields.Date.today()),
            ("is_overdue", "=", False),
        ])
        overdue.write({"is_overdue": True})

And the record goes in an XML data file listed in the manifest. This version is for Odoo 18 and 19:

<odoo noupdate="1">
    <record id="ir_cron_flag_overdue_rentals" model="ir.cron">
        <field name="name">Equipment Rental: flag overdue returns</field>
        <field name="model_id" ref="model_equipment_rental"/>
        <field name="state">code</field>
        <field name="code">model._cron_flag_overdue()</field>
        <field name="interval_number">1</field>
        <field name="interval_type">hours</field>
    </record>
</odoo>

noupdate="1" means an admin who changes the interval won’t have it reset on the next module update.

What changed in Odoo 18

Odoo 17 still has numbercall and doall. numbercall defaults to 1, meaning the job runs once and then deactivates itself, so 17 modules need <field name="numbercall">-1</field> for a job that repeats. doall controlled whether missed runs were replayed after downtime.

Both fields are gone from 18.0 onward. Leave numbercall in a record you port from 17 and the module won’t install, because the field doesn’t exist.

Odoo 18 also added failure tracking. According to the docs, a job that hits an error or a timeout three consecutive times is considered failed. After five consecutive failures spread over at least seven days, Odoo deactivates the job and notifies the admin.

Long jobs are expected to work in batches. Odoo 18 has _notify_progress(done=..., remaining=...) for this. Odoo 19 replaces it with _commit_progress(), which commits each batch and tells your loop how much time it has left, and marks the old method deprecated.

Where cron jobs go wrong

Copying cron XML across versions. A 17 record with numbercall breaks the install on 18. An 18 record without it installs fine on 17, runs once and switches itself off. Check the version before you paste a record from a tutorial.

One huge transaction. A job that processes 200,000 records in one call holds a worker and can hit the time limit. Batch it.

Calling the method directly to test it. The docs recommend method_direct_trigger() so the job runs the way the scheduler would run it.

Wondering why it never fired on staging. On Odoo.sh, staging databases are neutralized and scheduled actions are disabled there until you trigger or re-enable them.

Because the field list changed between 17 and 18, cron XML has to be written for the exact version the addon targets, which is one reason erpfly asks for your Odoo version before it writes anything. See Odoo module development for the rest of what’s in the addon, or our step-by-step module guide for where data files sit in the manifest.

Where ir.cron comes up

Terms used on this page

More Odoo terms

Your next module is one paragraph away

Write it the way you’d explain it to a new hire. We’ll turn it into an app you can read, test and install.

Odoo 19

Create your account

Free to start. No card needed.

By signing up you agree to our terms and privacy policy.