Một tuỳ biến cực kỳ hay gặp trong thực chiến: bạn muốn từ form Liên hệ của một khách, bấm một nút là xem ngay thẻ thành viên của họ. Nút đó không có sẵn — form Liên hệ thuộc app Contacts của Odoo. Để thêm nó, bạn ghép hai kỹ thuật đã học: inherit model (thêm method + field đếm vào res.partner) và inherit view (chèn nút vào form qua xpath). Kết quả là một "nút thống kê" (stat button) như những nút "12 Đơn hàng", "3 Cuộc họp" bạn thấy khắp Odoo. Bài này dựng nút đó thật.
Hai lớp: method ở model, nút ở view
# 1) Thêm method vào MODEL của form đó (res.partner)
class ResPartner(models.Model):
_inherit = 'res.partner'
so_the_thanh_vien = fields.Integer(compute='_compute_so_the')
def action_xem_the(self): # nút gọi tới
self.ensure_one()
return {'type': 'ir.actions.act_window', 'res_model': 'quan.the.thanh.vien',
'view_mode': 'list,form', 'domain': [('partner_id', '=', self.id)]}
<!-- 2) Extension view: chèn nút vào button_box của form Liên hệ -->
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_xem_the"
class="oe_stat_button" icon="fa-id-card-o"
invisible="so_the_thanh_vien == 0">
<field name="so_the_thanh_vien" widget="statinfo" string="Thẻ TV"/>
</button>
</xpath>

Hình 1: Hai lớp phối hợp. Model: action_xem_the trả về một act_window mở danh sách thẻ đã lọc theo partner_id của khách đang xem. View: chèn <button type="object" name="action_xem_the"> vào //div[@name='button_box'] (hộp nút thống kê ở đầu form). class="oe_stat_button" + widget="statinfo" biến nó thành nút thống kê (số + nhãn). invisible="so_the_thanh_vien == 0" ẩn nút khi khách chưa có thẻ.
Xem thật: nút trên form Liên hệ
Cài xong, mở form Liên hệ của Nguyễn Văn An — nút "Thẻ TV" xuất hiện trong hộp nút thống kê (form Liên hệ có sẵn nhiều nút nên nút mới nằm trong nhóm "Thêm"):

Hình 2: Nút thật. Nút "Thẻ TV: 1" (icon thẻ, số 1 = so_the_thanh_vien qua statinfo) do module quan_ca_phe thêm vào form Liên hệ của app Contacts. Vì form này đã có nhiều nút thống kê (Cơ hội, Bán hàng, Hóa đơn...), Odoo tự gom nút mới vào dropdown "Thêm". Bấm nút là mở danh sách thẻ của đúng khách này (nhờ domain lọc partner_id).
Cả hai điều quan trọng đều real: form của app khác giờ có nút của module mình, và nút bấm được — mở đúng dữ liệu liên quan.
Vì sao stat button hay dùng
Nút thống kê (oe_stat_button) là mẫu chuẩn của Odoo để liên kết bản ghi với dữ liệu liên quan:
- Nó hiện một con số (số bản ghi liên quan) — người dùng liếc là biết "khách này có 1 thẻ".
- Bấm vào mở danh sách đã lọc — không cần tự tìm.
- Đặt trong
button_boxđể Odoo tự bố trí gọn ở đầu form.
Đây chính là cách form Đơn hàng có nút "Hóa đơn", form Khách có nút "Cơ hội"... — tất cả là stat button gọi act_window với domain.
Đặt nút ở đâu
//div[@name='button_box']: hộp nút thống kê (góc phải, đầu form) — cho nút "xem dữ liệu liên quan".//header: đầu form — cho nút quy trình (Xác nhận, Duyệt, Huỷ) đổi trạng thái.- Chọn đúng chỗ theo ý nghĩa: thống kê/điều hướng → button_box; hành động quy trình → header.
Vài lưu ý Odoo 19
- Method của nút phải nằm ở model của form — ở đây là
res.partner, nênaction_xem_theviết trongResPartner(inheritres.partner), không phải model thẻ. widget="statinfo"hiển thị field số +stringlàm nhãn; cần field số (thường computed đếm).icon="fa-..."dùng Font Awesome cho biểu tượng nút thống kê.invisible="field == 0"ẩn nút khi không có dữ liệu — tránh nút "0" vô nghĩa.- Nút overflow vào "Thêm" khi button_box đầy là hành vi bình thường của Odoo, không phải lỗi.
Ba ý mang về
- Thêm nút vào form app khác = inherit model (method + field) + inherit view (chèn nút qua xpath). Method phải ở model của form đó; nút
type="object"gọi method, method trảact_windowmở dữ liệu liên quan (lọc bằngdomain). - Stat button (
class="oe_stat_button"+widget="statinfo") đặt trong//div[@name='button_box']là mẫu chuẩn "liên kết bản ghi ↔ dữ liệu liên quan": hiện số, bấm mở danh sách đã lọc. Đã chứng minh: nút "Thẻ TV: 1" trên form Liên hệ mở thẻ của đúng khách. - Chọn chỗ theo ý nghĩa:
button_boxcho thống kê/điều hướng,headercho nút quy trình; ẩn nút rỗng bằnginvisible; nút mới có thể gom vào dropdown "Thêm" khi hộp đầy.
Xong nhóm bài tuỳ biến view. Lần sau tới kỹ thuật nâng cao nhất về view — sinh view bằng code lúc chạy: Phần sau mổ xẻ view động qua get_view (trước là fields_view_get) — cách một model tự sửa cấu trúc view của mình lúc chạy, cho những trường hợp XML tĩnh không đủ.