Mở bất kỳ form nào trong Odoo — một liên hệ, một sản phẩm — bạn sẽ thấy một dãy nút hình hộp ở góc trên: "3 Cơ hội", "6 Đơn bán", "$12.350.000 Đã xuất hóa đơn". Đó là smart button: mỗi nút vừa hiện một con số tổng hợp, vừa là lối tắt bấm-là-nhảy sang danh sách bản ghi liên quan. Nhìn thì "xịn" nhưng cơ chế rất gọn. Bài này dựng một smart button từ đầu cho model thẻ thành viên trong module demo: nút "Lượt tích điểm" đếm số dòng lịch sử, bấm vào mở đúng danh sách của thẻ đó.

Ba mảnh ghép

Một smart button luôn gồm đúng ba thứ:

  1. Một field compute giữ con số muốn hiện (ở đây: số dòng lịch sử điểm).
  2. Một <button> trong <div name="button_box"> ở đầu <sheet>, class oe_stat_button, chứa field đó với widget="statinfo".
  3. Một method (type="object") trả về một dict ir.actions.act_window để mở danh sách liên quan.

Ảnh chụp đoạn mã nền tối gồm hai phần: phần XML đặt nút trong div button_box với button type object name action_xem_lich_su class oe_stat_button icon fa-history chứa field so_dong_lich_su widget statinfo; phần Python khai field Integer compute, method _compute_so_dong_lich_su gán len của lich_su_ids, và method action_xem_lich_su trả về dict act_window mở model quan lich su diem lọc theo the_id bằng self id

Hình 1: Ba mảnh ghép. so_dong_lich_su là field compute đếm len(lich_su_ids); nút trong button_box hiện nó qua widget="statinfo"; action_xem_lich_su trả act_window với domain=[('the_id','=', self.id)] để danh sách mở ra chỉ chứa dòng của đúng thẻ này.

Hai điểm đáng chú ý về cú pháp Odoo 19: dùng thẻ <button> với class="oe_stat_button" (không phải widget đặc biệt nào), và view_mode ghi list,form — chữ list chứ không phải tree như bản cũ.

Nút trên form thật

Sau khi thêm code và nâng cấp module, mở form thẻ VIP-0001. Nút xuất hiện ngay ở đầu form với con số thật:

Ảnh chụp giao diện Odoo thật của form thẻ thành viên VIP-0001, phía trên có smart button biểu tượng đồng hồ lịch sử ghi số 3 và nhãn Lượt tích điểm; bên dưới là tiêu đề Mã thẻ VIP-0001 cùng các nhóm Thông tin thẻ và Điểm và số dư, bên phải là khung trao đổi chatter

Hình 2: Smart button "Lượt tích điểm" hiện 3 — đúng bằng số dòng lịch sử của thẻ VIP-0001. Con số này là field compute, tự cập nhật khi thêm/bớt dòng lịch sử (nhờ @api.depends('lich_su_ids')). Icon fa-history là biểu tượng Font Awesome gắn qua thuộc tính icon.

Con số không phải gõ tay — nó được tính lại tự động mỗi khi lich_su_ids đổi, vì @api.depends đã khai phụ thuộc vào đúng field đó. Thêm một dòng lịch sử, số nhảy lên 4 ngay.

Bấm vào thì sao

Vì name="action_xem_lich_su" và type="object", bấm nút gọi method cùng tên. Method trả về một act_window có domain lọc theo the_id, nên Odoo mở một danh sách chỉ gồm dòng lịch sử của thẻ đang xem:

Ảnh chụp giao diện Odoo thật hiển thị danh sách Lịch sử điểm của VIP-0001 mở ra sau khi bấm nút, ba dòng với cột Ngày đều 16 tháng 9, cột Điểm cộng lần lượt 50 30 40, cột Ghi chú lần lượt Mua 5 ly cà phê Đổi combo Tết Giới thiệu bạn, chân bảng có tổng 120

Hình 3: Kết quả bấm nút — danh sách lịch sử điểm đã lọc sẵn cho VIP-0001 (3 dòng, tổng 120 điểm). Breadcrumb "Lịch sử điểm — VIP-0001" chính là name mà method đặt. context với default_the_id giúp nếu bấm "Mới" ở đây, dòng tạo ra tự gắn về đúng thẻ này.

Đây là điều làm smart button "thông minh": nó không chỉ hiện số, mà còn đưa người dùng thẳng tới đúng tập dữ liệu đằng sau con số đó, không phải tự đi lọc.

Vài lưu ý thực chiến

  • statinfo cần một field. widget="statinfo" hiển thị giá trị + nhãn string. Không có field bên trong thì nút trống trơn. Muốn hiện hai dòng số thì đặt hai field.
  • Nút bị ẩn khi không có gì để xem. Trên form liên hệ, nút "Thẻ TV" của demo có invisible="so_the_thanh_vien == 0" — khách chưa có thẻ thì giấu nút đi cho gọn. Đây là mẫu rất hay dùng.
  • Coi chừng button_box quá tải. Form res.partner chuẩn đã có nhiều nút sẵn; thêm nút của bạn vào có thể bị dồn vào menu "Thêm ▾". Trên model của chính mình thì button_box trống nên nút luôn hiện — như Hình 2.
  • _compute chạy cho mỗi bản ghi hiển thị. Đếm bằng len(self.lich_su_ids) ổn cho One2many; nhưng nếu đếm qua search_count trên bảng lớn và hiện nút đó trong danh sách hàng trăm dòng, hãy để ý số truy vấn.

Ba ý mang về

  1. Smart button = field compute + <button class="oe_stat_button"> trong button_box + method trả act_window. Ba mảnh ghép, khoảng 20 dòng.
  2. widget="statinfo" hiện con số, @api.depends khiến nó tự cập nhật; icon="fa-..." gắn biểu tượng. Cú pháp Odoo 19: <button> + view_mode="list,form".
  3. Giá trị thật nằm ở method: trả act_window với domain lọc theo bản ghi hiện tại để bấm nút là mở đúng danh sách liên quan; thêm context default_... để tạo mới ở đó tự gắn quan hệ.

Nút đã đẹp, giờ ta quay lại chuyện hiển thị field cho tinh tế hơn. Phần sau bàn về widget cho many2many — khi nào dùng tags, khi nào dùng checkboxes, và chúng đổi trải nghiệm nhập liệu thế nào.