Có những việc không nên đợi ai bấm nút: chuyển thẻ quá hạn sang "hết hạn", gửi nhắc gia hạn mỗi sáng, dọn dữ liệu tạm ban đêm. Odoo lo phần này bằng ir.cron — "tác vụ đã lên lịch". Bạn khai một record trỏ tới method của model, đặt chu kỳ, và scheduler của Odoo tự gọi định kỳ. Bài này dựng một cron rà soát hạn thẻ, test nó mà không phải chờ, và chỉ ra vài field Odoo 19 đã bỏ khiến code cũ gãy.

Method chạy nền: tự tìm bản ghi

Khác với action chạy trên bản ghi người dùng chọn, method của cron không nhận active_ids — nó tự đi tìm việc. Ở đây: tìm mọi thẻ đang dùng mà ngày cấp đã quá hạn, rồi chuyển hết hạn:

Ảnh chụp đoạn mã nền tối hai phần. Phần trên là method Python cron_ra_soat_han_the trên model, đặt HAN_NGAY bằng 12, tính han bằng fields Date today trừ timedelta HAN_NGAY ngày, search các thẻ trang_thai bằng dang_dung và ngay_cap nhỏ hơn hoặc bằng han, lặp qua ghi trang_thai het_han và message_post rồi log số thẻ và return len; chú thích self là recordset rỗng nên phải tự search. Phần dưới là file data cron_ra_soat.xml khai data noupdate 1 record ir.cron với name Rà soát hạn thẻ thành viên, model_id ref model_quan_the_thanh_vien, state code, code model chấm cron_ra_soat_han_the, interval_number 1, interval_type days; chú thích Odoo 19 bỏ numbercall và doall, nextcall có default now, biến model trong code là recordset rỗng

Hình 1: Method cron_ra_soat_han_the không có tham số — vì khi cron gọi, self là recordset rỗng, nên method phải tự search ra việc. Bên dưới là record ir.cron: model_id trỏ model, state="code" với code gọi chính method đó qua biến model (recordset rỗng của model). interval_number + interval_type đặt chu kỳ. Bọc trong <data noupdate="1"> để nâng cấp module không ghi đè phần người vận hành đã chỉnh tay (đổi giờ chạy, tắt bật).

Một điểm Odoo 19 đã đổi khiến nhiều ví dụ cũ gãy: numbercall và doall đã bị bỏ. Tôi khai numbercall theo thói quen và module không cài được ("Invalid field 'numbercall' in 'ir.cron'"). Bỏ nó đi là xong; nextcall thì có sẵn default là thời điểm hiện tại nên không cần khai.

Xem tác vụ đã lên lịch

Sau khi cài, cron hiện trong Cài đặt → Kỹ thuật → Tác vụ đã lên lịch:

Ảnh chụp form Tác vụ đã lên lịch của Odoo tên Rà soát hạn thẻ thành viên, có nút Chạy thủ công ở trên, các trường Mô hình là Thẻ thành viên, Người dùng Trình lên lịch là OdooBot, Thực thi Mỗi 1 Ngày, công tắc Đang hoạt động đang bật màu xanh, Ngày thực hiện tiếp theo 1 giờ 30 ngày 18 tháng 9, Mức độ ưu tiên 5, tab Mã hiển thị dòng lệnh model chấm cron_ra_soat_han_the, khung nhật ký ghi Scheduled Actions created bởi OdooBot

Hình 2: Cron dưới dạng form. Thực thi Mỗi 1 Ngày là chu kỳ; Đang hoạt động bật/tắt; Ngày thực hiện tiếp theo (nextcall) là lần chạy kế; Mức độ ưu tiên khi nhiều cron tới hạn cùng lúc. Tab Mã chứa model.cron_ra_soat_han_the(). Đáng chú ý nút Chạy thủ công — bấm là chạy ngay, cực tiện để thử mà không đợi tới giờ.

Test cron: đừng chờ scheduler

Chờ tới nextcall để biết cron đúng hay sai là cực hình. Cách thực dụng: gọi thẳng method trong odoo shell và đo kết quả:

Ảnh chụp phiên odoo shell nền tối, in trạng thái bốn thẻ trước khi chạy cho thấy VIP-0001 dang_dung VIP-0002 het_han VIP-0003 dang_dung VIP-0004 khoa với chú thích hôm nay 18 tháng 9, gọi The chấm cron_ra_soat_han_the trả về 1 với chú thích VIP-0001 cấp 03 tháng 9 quá 12 ngày, in lại trạng thái cho thấy VIP-0001 chuyển het_han còn VIP-0003 vẫn dang_dung với chú thích chỉ VIP-0001 đổi, và chú thích VIP-0003 cấp 17 tháng 9 mới một ngày nên không đụng và đã hoàn nguyên sau đo

Hình 3: Gọi thẳng The.cron_ra_soat_han_the() — nó trả về 1 (một thẻ chuyển hết hạn). Đo chính xác: VIP-0001 cấp ngày 03/09, quá 12 ngày so với hôm nay 18/09, nên bị chuyển; VIP-0003 cấp 17/09 mới một ngày nên không đụng tới. Logic lọc chạy đúng như thiết kế. (Đo xong tôi hoàn nguyên VIP-0001 về đang dùng để giữ dữ liệu demo.) Trên giao diện, nút "Chạy thủ công" làm đúng việc này mà không cần shell.

Vài lưu ý khi khai cron

  • noupdate="1" gần như bắt buộc cho ir.cron. Người vận hành hay chỉnh giờ chạy, tắt tạm khi bảo trì — nếu không noupdate, mỗi lần -u module là mọi chỉnh tay bị ghi đè về mặc định.
  • Method phải nhẹ và idempotent. Cron có thể chạy lại (server khởi động lại, chạy tay). Lọc đúng điều kiện (trang_thai='dang_dung') để chạy hai lần không gây tác dụng phụ — thẻ đã hết hạn thì lần sau không khớp nữa.
  • interval_type nhận minutes, hours, days, weeks, months. Đừng đặt phút quá nhỏ cho việc nặng — cron chạy trong worker nền, việc dài làm nghẽn các cron khác.
  • Cron chạy dưới user_id (mặc định OdooBot/superuser). Nếu cần quyền hạn chế hơn, đổi Scheduler User; nhưng thường để superuser cho nó thấy hết dữ liệu.

Ba ý mang về

  1. ir.cron là việc chạy nền theo lịch: một record trỏ model_id + state="code" gọi method của model; interval_number/interval_type đặt chu kỳ. Method tự search vì self/model là recordset rỗng.
  2. Odoo 19 đã bỏ numbercall và doall — code cũ khai chúng sẽ không cài được; nextcall có default nên không cần khai.
  3. Test bằng cách gọi thẳng method (hoặc nút "Chạy thủ công"), đừng chờ scheduler; bọc record trong <data noupdate="1"> để giữ chỉnh tay của người vận hành.

Cron này mới chỉ gọi một method. Nhưng ô Mã cho phép viết hẳn một đoạn Python ngay tại đó — với những biến và giới hạn riêng. Phần sau đào vào cron thực thi code Python: state="code", các biến env/model/log có sẵn, và sandbox chặn gì.