Ở phần trước, server action "Cộng 5 điểm tri ân" tự dưng xuất hiện trong menu ⚙ Tác vụ. Còn hai report thì nằm ở nút In. Không phải Odoo đoán mò — cả ba đều được bind vào model qua đúng ba trường, và chính ba trường đó quyết định loại action nào hiện ở menu nào, trên view nào. Hiểu chúng, bạn gắn được bất kỳ action nào vào đúng chỗ trong menu ngữ cảnh.

Ba trường quyết định tất cả

Bất kỳ action nào — server action, report, act_window, client action — đều gắn vào menu ngữ cảnh của một model bằng ba trường:

Ảnh chụp đoạn mã XML nền tối ba phần. Phần trên nhóm ba trường binding gồm binding_model_id ref model_quan_the_thanh_vien với chú thích gắn vào model nào, binding_type giá trị action với chú thích action ra menu Tác vụ còn report ra In, binding_view_types giá trị list phẩy form với chú thích hiện ở view nào. Phần giữa là server action action_server_tri_an model ir.actions.server khai binding_model_id và binding_view_types list form với tiêu đề server action ra menu Tác vụ do binding_type mặc định là action. Phần dưới là report action_report_the model ir.actions.report khai binding_model_id và binding_type report với tiêu đề report ra nút In bắt buộc binding_type report, kèm chú thích record khai binding phải nằm sau khi model đã có dùng ref model gạch dưới, gỡ khỏi menu thì xoá binding_model_id, và get_bindings model là registry đứng sau

Hình 1: Ba trường. binding_model_id nói action gắn vào model nào. binding_type quyết định menu nào: action (mặc định) đưa vào ⚙ Tác vụ, report đưa vào nút In. binding_view_types (list, form, kanban...) giới hạn action chỉ hiện ở những view đó. Bỏ một trong ba đi là action lạc chỗ hoặc biến mất khỏi menu.

Một điểm cú pháp quan trọng và dễ sai: <record> khai binding phải nạp sau khi model đã tồn tại, vì nó tham chiếu ref="model_quan_the_thanh_vien" — external id của model chỉ có sau khi model được đăng ký. Nếu file binding nạp quá sớm trong data của manifest, ref chưa phân giải được và module lỗi khi cài.

Cùng cơ chế, hai đích đến

binding_type chia action thành hai nhóm rơi vào hai menu khác nhau. Report có binding_type="report" rơi vào nút In:

Ảnh chụp giao diện danh sách Thẻ thành viên của Odoo, thanh trên ghi Tất cả 4 đã chọn, nút In đang mở dropdown liệt kê hai mục Thẻ thành viên và Sao kê điểm theo tháng, bên cạnh là nút Tác vụ chưa mở, danh sách nhóm theo hạng hiện Bạc mở ngoặc 3 và Vàng mở ngoặc 1

Hình 2: Nút In mở ra — bên trong là hai report đã bind: "Thẻ thành viên" và "Sao kê điểm theo tháng". Chúng không lẫn vào menu Tác vụ (chỗ của server action) vì binding_type="report" đẩy chúng sang đây. Hai rổ tách biệt, hai menu riêng, cùng một cơ chế binding phía sau.

Nhìn lại phần trước: server action "Cộng 5 điểm tri ân" nằm ở Tác vụ, còn hai report này ở In — chỉ khác nhau đúng giá trị binding_type.

Registry đứng sau: get_bindings

Web client không tự đoán menu. Nó gọi một hàm registry để lấy danh sách action đã bind cho model, rồi dựng menu từ đó. Bạn gọi được chính hàm đó trong shell:

Ảnh chụp phiên odoo shell nền tối, gọi env ir.actions.actions get_bindings của quan.the.thanh.vien gán vào b, lặp qua b action in ra Cộng 5 điểm tri ân list form với chú thích hiện ở Tác vụ, lặp qua b report in ra Thẻ thành viên list form và Sao kê điểm theo tháng list form với chú thích hiện ở nút In, kèm chú thích web client gọi đúng registry này khi dựng menu Tác vụ và In cho model và action với report là hai rổ tách biệt nên ra hai menu khác nhau

Hình 3: get_bindings('quan.the.thanh.vien') trả về một dict với các rổ 'action' và 'report' (và 'print'). Rổ action chứa server action, rổ report chứa hai report — đúng những gì thấy trên hai menu. Đây là nguồn sự thật mà giao diện đọc để dựng menu ngữ cảnh; muốn biết vì sao một action hiện/không hiện, soi hàm này là ra.

Vài lưu ý khi bind

  • binding_type mặc định là action. Server action và act_window không cần khai nếu muốn vào Tác vụ; report thì bắt buộc ghi report để vào nút In.
  • binding_view_types lọc theo view. Muốn một lệnh chỉ chạy khi mở một bản ghi (form) mà không cho chạy hàng loạt từ list, đặt binding_view_types="form" — nó biến mất khỏi menu Tác vụ của list.
  • Gỡ action khỏi menu = xoá binding_model_id. Action vẫn tồn tại và gọi được bằng code/menu, chỉ là không còn tự hiện trong menu ngữ cảnh nữa.
  • act_window cũng bind được để "mở danh sách liên quan" từ menu Tác vụ — không chỉ server action mới vào được đó.

Ba ý mang về

  1. Ba trường binding_model_id + binding_type + binding_view_types quyết định action của bạn hiện ở menu nào, trên view nào của model nào — đây là cách gắn bất kỳ action vào menu ngữ cảnh.
  2. binding_type chia đích đến: action (mặc định) → ⚙ Tác vụ; report → nút In. Cùng cơ chế, hai rổ tách biệt.
  3. get_bindings(model) là registry web client đọc để dựng menu; khai binding phải nằm sau khi model tồn tại (dùng ref="model_..."), và gỡ binding chỉ là xoá binding_model_id.

Đến đây ta đã điều khiển action theo người dùng bấm. Nhưng có những việc phải tự chạy không cần ai bấm — dọn dữ liệu ban đêm, gửi nhắc hạn thẻ mỗi sáng. Phần sau bước vào ir.cron — việc chạy định kỳ: khai một tác vụ nền, đặt chu kỳ, và gọi đúng method của model.