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>

Ảnh chụp mã XML và Python nền tối, phần đầu thẻ calendar string Lịch cấp thẻ có date_start ngay_cap color hang_the mode month chứa field name và partner_id, phần giữa lớp TheThanhVien kế thừa models Model với _inherit gồm quan chấm mixin chấm dau chấm vet cùng mail chấm thread và mail chấm activity chấm mixin để có activity, phần sau thẻ activity view string Việc trên thẻ chứa templates với div t-name activity-box hiển thị field name, cuối cùng dòng Python gọi tv chấm activity_schedule với mail_activity_data_todo date_deadline ngày 20 tháng 9 2026 và summary Gọi mời gia hạn thẻ VIP

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:

Ảnh chụp giao diện calendar Odoo 19 tháng 9 2026, lưới lịch theo tuần, thẻ VIP-0001 Bạc của Nguyễn hiện ở ngày 3, thẻ VIP-0002 Bạc ở ngày 10, thẻ VIP-0003 Vàng ở ngày 17 được tô màu tím khác với các thẻ Bạc màu cam, thẻ VIP-0004 Bạc ở ngày 24, ngày 17 là hôm nay được đánh dấu tròn đỏ, bên phải là lịch thu nhỏ của 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:

Ảnh chụp giao diện activity view Odoo 19 cho thẻ thành viên, hàng VIP-0001, các cột theo loại việc gồm Việc cần làm Email Gọi Cuộc họp Tài liệu, ô dưới cột Việc cần làm hiện một thẻ màu xanh lá Gọi mời gia hạn thẻ VIP hạn ngày 20 tháng 9 2026 kèm thanh tiến độ một trên một, phía dưới có nút Lên lịch hoạt động

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_start bắt buộc; thêm date_stop cho sự kiện có khoảng thời gian; date_delay cho độ dài. color chỉ tô màu, không lọc.
  • Kéo–thả trên calendar tự động write lạ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.mixin trướ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ề

  1. Calendar view hiện bản ghi trên lịch theo date_start (một field ngày), color tô 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.
  2. Activity view gom việc cần làm theo bản ghi × loại việc — nhưng model phải _inherit mail.activity.mixin (kèm mail.thread, và depends có 'mail'); thiếu là lỗi thiếu activity_ids.
  3. 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.