Không phải dữ liệu nào cũng hợp với bảng. Thẻ thành viên có ngày cấp — xem chúng trên lịch trực quan hơn nhiều. Và mỗi thẻ có thể kèm việc cần làm ("gọi mời gia hạn", "gửi email ưu đãi") — đó là việc của activity view. Hai loại view này gắn với thời gian và công việc, và activity có một điều kiện tiên quyết dễ quên: model phải kế thừa mixin của app Mail. Bài này dựng cả hai cho thẻ thành viên và lên lịch một việc thật.
Calendar: cần một field ngày làm mốc
Calendar view chỉ cần một field ngày (Date/Datetime) làm date_start. Thêm color để tô màu theo một field phân loại:
<calendar string="Lịch cấp thẻ" date_start="ngay_cap"
color="hang_the" mode="month"> <!-- màu theo hạng -->
<field name="name"/>
<field name="partner_id"/>
</calendar>

Hình 1: Khai báo. Calendar cần date_start là field ngày; color tô màu theo field phân loại; mode là chế độ mặc định (month/week/day). Activity cần điều kiện tiên quyết: model _inherit thêm mail.thread và mail.activity.mixin (nếu không, model không có field activity_ids và view sẽ lỗi). Dòng cuối lên lịch một việc bằng activity_schedule.
Calendar chạy thật: mỗi thẻ một ngày
Đặt ngày cấp khác nhau cho 4 thẻ và mở lịch tháng 9/2026:

Hình 2: Calendar chạy thật. Bốn thẻ hiện đúng ngày cấp của mình (3, 10, 17, 24 tháng 9). color="hang_the" tô màu theo hạng: thẻ Vàng (VIP-0003) màu tím, các thẻ Bạc màu cam — nhìn màu là biết hạng. Người dùng kéo–thả một thẻ sang ngày khác để đổi ngay_cap ngay trên lịch.
Activity: gom việc cần làm trên bản ghi
Activity view gom việc cần làm (activity) của từng bản ghi thành một bảng: hàng là bản ghi, cột là loại việc (gọi, email, họp...). Mình lên lịch một việc thật trên VIP-0001 rồi mở activity view:

Hình 3: Activity view chạy thật. Hàng VIP-0001, các cột theo loại việc (Việc cần làm, Email, Gọi, Cuộc họp, Tài liệu). Việc "Gọi mời gia hạn thẻ VIP" (hạn 20/09/2026) mình vừa lên lịch hiện dưới cột "Việc cần làm" (màu xanh = còn hạn). Bấm "Lên lịch hoạt động" để thêm việc mới cho bất kỳ thẻ nào.
Điều kiện tiên quyết của activity: mail.activity.mixin
Đây là chỗ hay vấp: activity không tự nhiên có. Model phải kế thừa mail.activity.mixin (thường đi kèm mail.thread) thì mới có field activity_ids và cơ chế lên lịch việc:
class TheThanhVien(models.Model):
_name = 'quan.the.thanh.vien'
_inherit = ['quan.mixin.dau.vet', 'mail.thread', 'mail.activity.mixin']
Và manifest phải khai depends có 'mail'. Thiếu mixin thì activity view báo lỗi "không có field activity_ids". Sau khi có mixin, lên lịch việc bằng code hoặc nút giao diện:
tv.activity_schedule('mail.mail_activity_data_todo',
date_deadline=date(2026, 9, 20), summary='Gọi mời gia hạn thẻ VIP')
mail.thread còn cho model một khung trao đổi (chatter) và theo dõi thay đổi — thường ta thêm cả hai mixin cùng lúc.
Vài lưu ý Odoo 19
- Calendar:
date_startbắt buộc; thêmdate_stopcho sự kiện có khoảng thời gian;date_delaycho độ dài.colorchỉ tô màu, không lọc. - Kéo–thả trên calendar tự động
writelại field ngày — tiện nhưng nhớ rằng nó là một thao tác ghi thật. - Activity view cần dữ liệu hoạt động; model không có activity thì view trống. Luôn đảm bảo
mail.activity.mixintrước. activity_schedule(xmlid, ...)dùng external id của loại hoạt động (mail.mail_activity_data_todo,..._call,..._email).
Ba ý mang về
- Calendar view hiện bản ghi trên lịch theo
date_start(một field ngày),colortô màu theo phân loại — đã chứng minh: 4 thẻ đúng ngày cấp, thẻ Vàng tím / Bạc cam. Kéo–thả đổi ngày ghi thật vào field. - Activity view gom việc cần làm theo bản ghi × loại việc — nhưng model phải
_inheritmail.activity.mixin(kèmmail.thread, vàdependscó'mail'); thiếu là lỗi thiếuactivity_ids. - Lên lịch việc bằng
activity_schedule(xmlid, date_deadline, summary)— đã chứng minh: việc "Gọi mời gia hạn thẻ VIP" hạn 20/09 hiện trong activity view.
Xong toàn bộ các loại view. Lần sau quay lại chi tiết trong form/list: Phần sau mổ xẻ cách làm field ẩn/chỉ đọc/bắt buộc theo điều kiện (invisible, readonly, required với biểu thức domain) — để form tự thay đổi theo dữ liệu người dùng nhập.