Có những thông tin không phải ai cũng nên thấy: số dư ví, giá vốn, ghi chú nội bộ. Và có những nút không phải ai cũng nên bấm: duyệt, khoá, huỷ. Odoo cho phép ẩn từng phần tử của form theo nhóm người dùng chỉ bằng một thuộc tính: groups. Đặt groups="..." lên một field, nút, tab hay khối, và ai không thuộc nhóm đó sẽ không thấy phần tử ấy. Bài này đặt groups lên vài phần tử rồi cho hai user khác nhóm mở cùng một form — họ thấy hai thứ khác nhau.

groups trên bất kỳ phần tử view nào

<!-- Chỉ admin (base.group_system) thấy số dư -->
<field name="so_du" groups="base.group_system"/>

<!-- Chỉ admin thấy nút Cộng điểm -->
<button name="..." type="action" groups="base.group_system"/>

<!-- Đặt trên field / button / page / group / div đều được -->
<page string="Nội bộ" groups="base.group_system"> ... </page>

Ảnh chụp mã XML nền tối minh hoạ thuộc tính groups, field so_du có groups base chấm group_system chỉ admin thấy, button có groups base chấm group_system, thẻ page và thẻ group cũng có groups, phần chú thích liệt kê các nhóm hay dùng gồm base chấm group_user nhân viên nội bộ base chấm group_system quản trị base chấm group_no_one chỉ hiện chế độ nhà phát triển, và ghi chú quan trọng phân biệt hai lớp groups trên field XML chỉ ẩn giao diện còn groups trên field trong Python models là bảo mật server-side thật

Hình 1: groups="module.group_xmlid" đặt được trên mọi phần tử view — field, button, page (tab), group, div. Ai không thuộc nhóm sẽ không thấy phần tử đó. Ghi giá trị là external id của nhóm (như base.group_system). Ghi chú cuối là điểm quan trọng nhất, sẽ nói ngay dưới đây.

Xem thật: hai user, hai form khác nhau

Cùng thẻ VIP-0001, mở bằng admin (thuộc base.group_system):

Ảnh chụp form Odoo thẻ VIP-0001 khi đăng nhập bằng admin, header có nút Cộng điểm ở góc trái và dải statusbar, cột Điểm và số dư bên phải hiển thị Điểm tích luỹ 150 phần trăm, Số dư 0 đô la, Tiền tệ USD và Ưu đãi áp dụng, avatar góc phải trên là chữ A của admin

Hình 2: Admin thấy đầy đủ: field "Số dư" ($0,00) trong cột phải, và nút "Cộng điểm..." trên header. Avatar góc phải là "A" (admin).

Giờ mở cùng form đó bằng một nhân viên thường (chỉ có base.group_user, không có base.group_system):

Ảnh chụp form Odoo cùng thẻ VIP-0001 khi đăng nhập bằng nhân viên thường, header KHÔNG còn nút Cộng điểm chỉ còn statusbar, cột Điểm và số dư bên phải chỉ hiển thị Điểm tích luỹ 150 phần trăm Tiền tệ USD và Ưu đãi áp dụng, KHÔNG có dòng Số dư, avatar góc phải trên là chữ N của nhân viên

Hình 3: Cùng form, nhân viên thường thấy ít hơn: field "Số dư" biến mất khỏi cột phải, nút "Cộng điểm..." biến mất khỏi header. Avatar là "N" (nhân viên). Đây là cùng một view XML — chỉ khác người xem.

Không cần hai form riêng, không cần logic điều kiện phức tạp. Một view, groups lo phần "ai thấy gì".

groups ở view vs ở model: khác biệt sống còn

Đây là điểm quan trọng nhất, và cũng dễ hiểu sai nhất:

  • groups trên <field> trong XML (view): ẩn field ở giao diện cho người không thuộc nhóm. Trong nhiều trường hợp Odoo cũng chặn đọc field đó, nhưng đây trước hết là chuyện hiển thị.
  • groups trên định nghĩa field trong Python (models): đây là bảo mật server-side thật. Field khai groups="base.group_system" trong model thì người ngoài nhóm không đọc được qua bất kỳ con đường nào — API, export, view khác — chứ không chỉ ẩn trên một form.

Quy tắc: thông tin nhạy cảm thật sự phải khai groups ở model, không chỉ ở view. Ẩn ở view mà không ẩn ở model thì một người biết dùng developer tools hay API vẫn đọc được. groups ở view chỉ để dọn giao diện cho gọn theo vai trò.

Các nhóm hay dùng

  • base.group_user: nhân viên nội bộ — hầu hết người dùng backend đều có. Đặt groups="base.group_user" để ẩn khỏi khách portal.
  • base.group_system: nhóm Cài đặt/Quản trị (admin). Dùng cho cấu hình, dữ liệu nhạy cảm.
  • base.group_no_one: nhóm "kỹ thuật" — phần tử chỉ hiện khi bật Chế độ nhà phát triển. Hợp cho field debug.
  • Nhóm của app: như account.group_account_manager, sale.group_sale_manager — cho phân quyền theo chức năng.

Vài lưu ý Odoo 19

  • Nhiều nhóm: groups="base.group_system,account.group_account_manager" → thấy nếu thuộc một trong các nhóm (OR).
  • Phủ định: groups="!base.group_portal" (dấu !) → ẩn với ai thuộc nhóm đó.
  • Đặt trên <page> để ẩn cả tab, trên <group> để ẩn cả cột — không cần đặt từng field.
  • groups khác invisible: invisible ẩn theo giá trị dữ liệu (điều kiện domain); groups ẩn theo nhóm người dùng. Kết hợp được cả hai.

Ba ý mang về

  1. groups="module.group_xmlid" ẩn field/nút/tab/group với ai không thuộc nhóm — đặt được trên mọi phần tử view. Đã chứng minh: cùng form VIP-0001, admin thấy "Số dư" + nút "Cộng điểm", nhân viên thường thì không.
  2. Phân biệt sống còn: groups ở view chỉ ẩn giao diện; groups trên field trong model (Python) mới là bảo mật server-side thật (chặn đọc qua mọi đường). Dữ liệu nhạy cảm phải khai ở model.
  3. Nhóm hay dùng: base.group_user (nhân viên), base.group_system (admin), base.group_no_one (chế độ dev); hỗ trợ nhiều nhóm (OR) và phủ định (!). groups ẩn theo người, invisible ẩn theo dữ liệu.

Ta đã đi hết một form nghiệp vụ đầy đủ với view, widget, nút, quy trình, chatter và phân quyền hiển thị. Lần sau quay lại QWeb — nhưng lần này ở phía backend: Phần sau mổ xẻ QWeb backend và các directive t-* (t-if, t-foreach, t-esc, t-call...) — nền tảng để dựng template cho kanban, báo cáo và email.