Mở bất kỳ form nào trong Odoo — đơn hàng, liên hệ, sản phẩm — bạn sẽ thấy chung một khuôn: một tấm "giấy" trắng bo góc ở giữa, tiêu đề lớn trên cùng, các field xếp thành hai cột, và vài tab ở dưới. Cái nhất quán đó không đến từ CSS bạn viết, mà từ vài thẻ bố cục XML mà Odoo quy ước. Nắm được sheet, group, notebook là bạn dựng được form chuẩn cho mọi model. Bài này dựng một form thật cho thẻ thành viên rồi xem nó hiện ra sao trên giao diện.
Ba khối bố cục cốt lõi
<form string="Thẻ thành viên">
<sheet> <!-- khung giấy trắng, giữa trang -->
<div class="oe_title"> <!-- tiêu đề lớn ở đầu form -->
<h1><field name="name"/></h1>
</div>
<group> <!-- group MẸ: chia 2 CỘT -->
<group string="Thông tin thẻ"> <!-- cột trái -->
<field name="partner_id"/>
<field name="hang_the"/>
</group>
<group string="Điểm và số dư"> <!-- cột phải -->
<field name="diem"/>
<field name="uu_dai_ids" widget="many2many_tags"/>
</group>
</group>
<notebook> <!-- tab ở dưới -->
<page string="Lịch sử điểm">...</page>
<page string="Ghi chú">...</page>
</notebook>
</sheet>
</form>

Hình 1: Cấu trúc XML. Bốn khối cần nhớ: <sheet> (khung giấy), <div class="oe_title"> với <h1> (tiêu đề lớn), <group> lồng <group> (chia cột), và <notebook>/<page> (tab). Field đặt trong group con tự căn nhãn – giá trị thành hàng gọn.
Xem nó hiện ra: form thật trên giao diện
Cài view này vào module rồi mở bản ghi VIP-0001, đây là kết quả thật trên Odoo 19:

Hình 2: Cùng đoạn XML ở Hình 1, render ra giao diện. Thấy rõ từng khối: oe_title thành "Mã thẻ / VIP-0001" cỡ lớn; group lồng group thành hai cột "Thông tin thẻ" và "Điểm và số dư" căn nhãn – giá trị thẳng hàng; many2many_tags biến uu_dai_ids thành các thẻ bo tròn; notebook thành hai tab với bảng lịch sử điểm sửa được ngay trong tab.
Điều đáng chú ý: bạn không viết một dòng CSS nào. Chỉ cần xếp field vào đúng khối, Odoo lo toàn bộ căn lề, khoảng cách, và giao diện đáp ứng (responsive).
Quy tắc bố cục cần thuộc
<sheet>là bắt buộc cho form "hồ sơ". Nó tạo tấm giấy trắng bo góc giữa trang. Không có sheet, form trải phẳng full-width trông thô.- Hai cột đến từ
<group>lồng<group>. Group mẹ chia không gian thành các cột; mỗi group con là một cột. Field trực tiếp trong group con tự xếp thành cặp nhãn – giá trị. Muốn một cột thì chỉ một group con; muốn ba cột thì ba group con. stringtrên group con thành tiêu đề mục (như "Thông tin thẻ"). Bỏstringthì cột không có tiêu đề.<notebook>/<page>cho nội dung phụ hoặc dài (lịch sử, dòng chi tiết, ghi chú) — đừng nhồi hết lên phần group chính khiến form dài lê thê.oe_title+<h1>dành cho field định danh chính (tên, mã) — đặt nó nổi bật trên cùng thay vì lẫn vào group.
Vài lưu ý Odoo 19
- Field lịch sử/dòng con dùng
<list>trong<field>(Odoo 19 gọi làlist, không còntree). Thêmeditable="bottom"để sửa ngay trong tab. - Nút thao tác và trạng thái thường đặt trong
<header>(đầu form, trên sheet) — sẽ bàn ở bài về nút và statusbar. widgetđổi cách hiển thị field:many2many_tagscho quan hệ nhiều-nhiều thành thẻ,monetarycho tiền,imagecho ảnh. Chọn widget đúng làm form vừa gọn vừa dễ dùng.
Ba ý mang về
- Form đẹp của Odoo đến từ bố cục XML, không phải CSS:
<sheet>làm khung giấy,oe_title+<h1>làm tiêu đề lớn,<group>lồng<group>chia cột,<notebook>/<page>gom tab. - Hai cột = một
groupmẹ chứa haigroupcon; field trực tiếp trong group con tự thành cặp nhãn – giá trị thẳng hàng;stringtrên group con là tiêu đề mục. - Đặt nội dung dài/phụ vào
notebook, chọnwidgetphù hợp (many2many_tags,monetary...) — Odoo lo hết căn lề và responsive, bạn chỉ cần xếp đúng khối. Đã dựng và chụp form thật cho thẻ VIP-0001.
Xong form, ta sang loại view hay dùng thứ hai: Phần sau mổ xẻ list view — các thuộc tính hữu ích, cách hiện tổng cột (sum) ở chân bảng, và tô màu dòng theo điều kiện.