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>

Ảnh chụp mã XML nền tối định nghĩa một form Odoo, thẻ form bọc thẻ sheet là khung giấy trắng bo góc ở giữa trang, bên trong có div class oe_title chứa label và h1 với field name làm tiêu đề lớn, tiếp theo một group mẹ chia hai cột gồm group string Thông tin thẻ ở cột trái chứa partner_id và hang_the và group string Điểm và số dư ở cột phải chứa diem và uu_dai_ids dùng widget many2many_tags, cuối cùng là notebook chứa hai page Lịch sử điểm và Ghi chú

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:

Ảnh chụp giao diện Odoo 19 hiển thị form thẻ thành viên VIP-0001, trên cùng là breadcrumb Thẻ thành viên và tiêu đề VIP-0001 Bạc Nguyễn Văn An, thân form có tiêu đề lớn Mã thẻ VIP-0001, bên dưới chia hai cột nhóm Thông tin thẻ bên trái với Khách hàng Nguyễn Văn An Hạng thẻ Bạc Trạng thái Mới cấp Ngày cấp 17 tháng 9, và nhóm Điểm và số dư bên phải với Điểm tích luỹ 120 Số dư 0 đô la Tiền tệ USD Ưu đãi áp dụng ba thẻ tag Giảm 10% đồ uống Freeship nội thành Tặng bánh sinh nhật, phía dưới là hai tab Lịch sử điểm và Ghi chú với bảng lịch sử ba dòng ngày điểm cộng ghi chú và nút Thêm một dòng

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.
  • string trên group con thành tiêu đề mục (như "Thông tin thẻ"). Bỏ string thì 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òn tree). Thêm editable="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_tags cho quan hệ nhiều-nhiều thành thẻ, monetary cho tiền, image cho ảnh. Chọn widget đúng làm form vừa gọn vừa dễ dùng.

Ba ý mang về

  1. 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.
  2. Hai cột = một group mẹ chứa hai group con; field trực tiếp trong group con tự thành cặp nhãn – giá trị thẳng hàng; string trên group con là tiêu đề mục.
  3. Đặt nội dung dài/phụ vào notebook, chọn widget phù 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.