Перейти до основного контенту
База знань · виробничий облік USD41,42 ПДВ20% МЗП8 000

Odoo

Модуль

Cron і scheduled actions — нічна логіка Odoo

ir.cron, model_id, function_id. Які стандартні cron важливі. Як писати власні — для перевірки ЄРПН-статусу, нагадувань, переоцінок.

Шлях у меню
Налаштування → Технічні → Заплановані дії
Технічна модель
ir.cron
Редакція
2026-05-28

Що це і навіщо

Половина Odoo живе у фоні. Поки бухгалтер виставляє інвойси, в системі тихо працюють cron’и: оновлюють курс НБУ, перевіряють статус ПН в ЄРПН, генерують нагадування про оплату, переоцінюють залишки, запускають reordering rules, чистять застарілі вкладення. Без них Odoo — лише форма для введення даних, а не жива система.

Усе це — ir.cron (Scheduled Actions). Прості записи в БД, які описують: коли запускати, який метод викликати, як часто, скільки спроб. Спрощено — це cron з crontab, тільки керований через ORM.

Як це працює

Модель:

class IrCron(models.Model):
    _name = 'ir.cron'
    name = fields.Char()
    model_id = fields.Many2one('ir.model')
    state = fields.Selection([('code', 'Python Code')])
    code = fields.Text()  # рядок типу "model.cron_clean_old_logs()"
    interval_number = fields.Integer(default=1)
    interval_type = fields.Selection([
        ('minutes', 'Minutes'),
        ('hours', 'Hours'),
        ('days', 'Days'),
        ('weeks', 'Weeks'),
        ('months', 'Months'),
    ])
    nextcall = fields.Datetime()  # коли наступний запуск
    numbercall = fields.Integer(default=-1)  # -1 = безкінечно
    doall = fields.Boolean()  # чи виконати пропущені прогони
    active = fields.Boolean(default=True)

Перебіг:

  1. Odoo має базовий процес cron worker (відокремлений від web/api workers).
  2. Він раз на хвилину сканує ir.cron де active=True AND nextcall<=now().
  3. Для кожного знайденого — виконує code від імені користувача user_id (за замовчуванням — суперюзер).
  4. Оновлює nextcall = now() + interval.

Метод викликається в контексті ORM, тож усередині доступне все звичне — self.env, пошук, запис. Якщо метод впав з винятком, cron спробує ще раз на наступному циклі; за numbercall > 0 спроби рахуються до вичерпання.

# Свій cron — перевіряти статус ПН в ЄРПН
def _cron_check_erpn_status(self):
    pending = self.env['tax.invoice.ua'].search([('state', '=', 'sent')])
    for pn in pending:
        new_state = self._erpn_api.check_status(pn.erpn_id)
        if new_state != pn.state:
            pn.state = new_state
            if new_state == 'registered':
                pn.message_post(body=_('ПН зареєстрована в ЄРПН'))
<!-- Запис у XML -->
<record id="cron_check_erpn" model="ir.cron">
  <field name="name">ЄРПН: перевірка статусу ПН</field>
  <field name="model_id" ref="model_tax_invoice_ua"/>
  <field name="state">code</field>
  <field name="code">model._cron_check_erpn_status()</field>
  <field name="interval_number">15</field>
  <field name="interval_type">minutes</field>
  <field name="active" eval="True"/>
</record>

Шлях у меню (developer mode): Settings → Technical → Automation → Scheduled Actions.

У контексті українського обліку

Стандартні cron, які точно потрібні в UA-проєкті:

  1. Update Currency Rate. Щодня тягне курс НБУ. Без цього експортно-імпортні операції рахуються за неправильним курсом.
  2. Anglo-Saxon Periodic COGS. Закриває WIP-проводки виробничого циклу. Без цього собівартість «зависає».
  3. Reordering rules. Запускає purchase requisitions під min-stock.
  4. Payment Reminders. Шле нагадування про прострочку.
  5. Mass Mailing — Queue. Якщо використовуєш email-розсилки.

Що треба дописати самим:

  • ЄРПН — перевірка статусу. Раз на 15 хвилин, для PN/RK у статусі sent.
  • СЕА — оновлення ліміту. Раз на годину, тягне з ДПС.
  • Реєстр ЄРПН — порівняння внутрішніх ПН з реєстром. Раз на день, для виявлення розходжень.
  • Контроль термінів реєстрації ПН. Щодня вранці, якщо до дедлайну менше 3 днів — нагадування бухгалтеру.
  • Курсова переоцінка на дату балансу. На останній день місяця — автоматична переоцінка валютних залишків. Перевірити, що НБУ є.
  • Перевірка прострочки авансових звітів. Щодня. Якщо підзвіт прострочений (більше 10 робочих днів — для відрядження або 5 — для готівки) — алерт співробітнику і бухгалтеру.
  • Закриття місяця — чек-лист. На 1 число — нагадування з чек-листом: WIP закрито, переоцінка зроблена, ПДВ збалансовано, lock date встановлено.

Як ми робимо (defense-контекст)

ТОВ «УкрПром-Тех» має такий парк власних cron:

CronПеріодичністьПризначення
Check ERPN status15 хвСтатус ПН/РК в ЄРПН
Update SEA balance1 годинаΣНакл з кабінету ДПС
Reconcile ERPN registry1 день, 02
Порівняння внутрішніх ПН з реєстром
ПН deadline warning1 день, 09
Нагадування за 3 дні до 15-денного дедлайну
Defense contract status1 день, 06
Тягне статуси контрактів з Прозоро.МО
Calculate WIP for defense MOs1 день, 23
Розрахунок реальної собівартості для відкритих захищених MO
Check ВТП export licenses1 день, 07
Перевірка термінів дозволів ДСЕКУ
Currency revaluation (auto)last day of monthПереоцінка валютних залишків

Особливість: cron, що ходять до зовнішніх API (ЄРПН, ДПС, Прозоро), мають retry-логіку і таймаути. Якщо API недоступне — не падаємо, а логуємо у mail.activity для адміна.

Типові граблі

  • Cron не запускається. Перевір: 1) active=True; 2) nextcall не залишився далеко в минулому (за doall=False Odoo такий прогін просто пропустить); 3) процес cron worker запущено (--no-cron його вимикає); 4) користувач, від якого йде запуск, має права на цю модель.
  • Cron «застрягає». Метод працює довше, ніж interval. Наступний запуск чекає, поки попередній закінчиться. Якщо це cron на годину, а виконується 90 хв — частина запусків просто пропаде. Або скорочуй роботу, або запускай частіше і з batching (по 100 записів за раз).
  • Cron впав мовчки. Виняток поглинула внутрішня логіка cron worker. Дивися в mail.message від моделі — Odoo іноді логує. Краще — обгортай весь код у try/except і пиши в _logger.error + створюй mail.activity.
  • Конкурентні cron. Два cron одночасно пишуть в одну таблицю. Транзакції конфліктують, один результат втрачається. Використовуй with_lock або синхронізацію через lock-файл у ir.attachment.
  • Бойовий cron відпрацював на тестовій копії. Скопіювали БД, а зовнішні cron не вимкнули — test-копія повторно зареєструвала ПН у реальному ЄРПН, і дублікат отримав статус «реєстрація вже існує». Завжди вимикай зовнішні cron при клонуванні БД (active=False).
  • numbercall лишився 1 (або його забули виставити). Cron спрацював один раз і назавжди замовк. Перевір: для постійного завжди -1.
  • Cron логи розпухли. Якщо метод щохвилини пише в _logger.info — за тиждень логи на 10 GB. Логуй тільки помилки і важливі стани, а не кожен тік.
  • Cron-користувач без 2FA — а аудитор про це питає. Cron’и зазвичай ходять від суперюзера, у якого 2FA немає. Заведи окремих service_user із мінімальними правами під конкретні cron — і security audit пройде спокійно.

Дотичне

Де цей модуль зустрічається3