Một form tốt biết tự thay đổi theo dữ liệu: thẻ hạng Vàng thì hiện thêm ô ghi chú nội bộ; thẻ đủ điểm VIP thì bắt buộc gán khách hàng; thẻ đã khoá thì không cho sửa số dư. Trong Odoo, bạn làm những điều này không cần JavaScript — chỉ cần đặt một biểu thức điều kiện vào các thuộc tính invisible, readonly, required của field. Và Odoo 19 đã đổi hẳn cú pháp so với các bản cũ, gọn hơn nhiều. Bài này dựng thật và xem form đổi theo dữ liệu.

Cú pháp mới của Odoo 19: biểu thức thẳng vào thuộc tính

Ở Odoo cũ, bạn viết attrs="{'invisible': [('hang_the', '!=', 'vang')]}" — dài dòng và khó đọc. Odoo 19 bỏ hẳn attrs: đặt biểu thức Python trực tiếp vào thuộc tính:

<!-- REQUIRED động: bắt buộc nhập khách khi thẻ từ 100 điểm -->
<field name="partner_id" required="diem >= 100"/>

<!-- INVISIBLE động: ô ghi chú chỉ hiện với thẻ Vàng -->
<field name="ghi_chu_noi_bo" invisible="hang_the != 'vang'"/>

<!-- READONLY động: khoá số dư khi thẻ đã khoá -->
<field name="so_du" readonly="trang_thai == 'khoa'"/>

Ảnh chụp mã XML nền tối minh hoạ cú pháp mới Odoo 19, có ghi chú rằng đặt biểu thức Python trực tiếp vào thuộc tính không còn attrs kiểu cũ, field partner_id có required diem lớn hơn hoặc bằng 100, field ghi_chu_noi_bo có invisible hang_the khác vang, field so_du có readonly trang_thai bằng khoa, phần chú thích liệt kê các toán tử dùng được như bằng khác lớn nhỏ in and or not cùng ví dụ invisible state not in draft sent và readonly is_locked or amount lớn hơn một triệu, cuối cùng ví dụ đặt invisible trên thẻ group để ẩn cả khối

Hình 1: Cú pháp Odoo 19. Đặt biểu thức Python thẳng vào invisible/readonly/required. Biểu thức đánh giá trên field của bản ghi đang mở, dùng toán tử == != > < >= in not in and or not. Muốn ẩn/khoá cả một khối (group, page, div), đặt thuộc tính trên thẻ bao ngoài thay vì từng field.

Xem thật: cùng form, hai hạng thẻ khác nhau

Cùng một form, mở thẻ Vàng (150 điểm) và thẻ Bạc (85 điểm) cho ra hai giao diện khác nhau. Đây là thẻ Vàng:

Ảnh chụp form Odoo thẻ VIP-0003 hạng Vàng 150 điểm, nhãn Khách hàng hiển thị màu đỏ và ô nhập tô nền hồng nhạt báo hiệu bắt buộc nhập vì thẻ từ 100 điểm, bên dưới có ô Ghi chú nội bộ hiện ra với gợi ý chỉ thẻ Vàng mới hiện ô này, cột phải hiện Điểm tích luỹ 150 Số dư 0 đô la Tiền tệ USD

Hình 2: Thẻ Vàng, 150 điểm. Hai điều kiện cùng kích hoạt: required="diem >= 100" làm nhãn "Khách hàng" đỏ và ô bắt buộc nhập (150 ≥ 100); invisible="hang_the != 'vang'" cho ô "Ghi chú nội bộ" hiện ra (vì là hạng Vàng). Form phản ứng theo đúng giá trị của bản ghi.

Giờ mở thẻ Bạc (85 điểm) — cùng form đó, khác hẳn:

Ảnh chụp form Odoo thẻ VIP-0002 hạng Bạc 85 điểm, nhãn Khách hàng hiển thị màu đen bình thường không bắt buộc vì thẻ dưới 100 điểm, KHÔNG có ô Ghi chú nội bộ vì thẻ không phải hạng Vàng, các trường còn lại gồm Hạng thẻ Bạc Trạng thái Đang dùng Ngày cấp 10 tháng 9 và cột phải Điểm tích luỹ 85 Số dư 0 đô la

Hình 3: Thẻ Bạc, 85 điểm, cùng form Hình 2. Khác biệt: nhãn "Khách hàng" màu đen bình thường (85 < 100 nên không bắt buộc), và ô "Ghi chú nội bộ" biến mất (không phải hạng Vàng). Không có JavaScript, không hai form riêng — chỉ một form với biểu thức điều kiện.

Ba thuộc tính, ba tác dụng

  • invisible="biểu_thức": ẩn field khi biểu thức đúng. Đặt trên thẻ bao (group, page, notebook, div) để ẩn cả khối. Lưu ý: field ẩn vẫn tồn tại trong dữ liệu — chỉ không hiện.
  • readonly="biểu_thức": khoá không cho sửa khi đúng. Giá trị vẫn hiển thị, chỉ không nhập được.
  • required="biểu_thức": bắt buộc nhập khi đúng — nhãn đỏ, không lưu được nếu để trống.

Biểu thức đọc field trên chính bản ghi đang mở, và Odoo tự cập nhật khi người dùng đổi giá trị: sửa diem từ 90 lên 100 ngay trên form, nhãn "Khách hàng" lập tức đỏ lên — không cần lưu.

Vài lưu ý Odoo 19

  • Field dùng trong biểu thức phải có trong view. Nếu invisible tham chiếu hang_the thì hang_the phải là một field trong form (có thể để invisible="1" nếu không muốn hiện nó).
  • invisible="1" (hằng) ẩn vĩnh viễn — hay dùng để nạp field phục vụ biểu thức khác mà không hiện ra.
  • Kết hợp điều kiện bằng and/or/not và ngoặc: readonly="state == 'done' or is_locked".
  • Với list view, cùng cú pháp áp cho cột: <field name="x" readonly="..."/> khoá ô theo dòng; column_invisible="..." ẩn cả cột.
  • Đây là thay đổi lớn khi nâng cấp từ Odoo cũ: mọi attrs="{...}" phải viết lại thành thuộc tính trực tiếp.

Ba ý mang về

  1. Odoo 19 bỏ attrs: đặt biểu thức Python thẳng vào invisible/readonly/required trên field (hoặc thẻ bao để áp cả khối). Gọn và dễ đọc hơn cú pháp cũ.
  2. Biểu thức đánh giá trên field của bản ghi đang mở và tự cập nhật khi người dùng nhập — đã chứng minh: cùng form, thẻ Vàng 150đ hiện ô ghi chú + bắt buộc khách (nhãn đỏ), thẻ Bạc 85đ ẩn ô ghi chú + không bắt buộc.
  3. invisible ẩn (dữ liệu vẫn còn), readonly khoá sửa, required bắt buộc nhập; field dùng trong biểu thức phải có mặt trong view (có thể invisible="1").

Nói tới điều kiện hiển thị, còn một cách "tô màu theo điều kiện" nữa dành riêng cho list: Phần sau đi sâu decoration — tô màu và kiểu chữ cho từng dòng list theo biểu thức, để bảng dữ liệu tự làm nổi bật thứ cần chú ý.