Người dùng muốn xem thẻ gom theo hạng, trong mỗi hạng lại chia theo trạng thái, và mỗi nhóm tự cộng tổng điểm. Nghe như một báo cáo phải code, nhưng Odoo làm được chỉ bằng khai báo. Có ba tầng điều khiển thứ tự và nhóm: _order của model, default_order của list, và group_by của search view. Hiểu chúng phối hợp thế nào là bạn kiểm soát trọn cách dữ liệu được sắp và gom. Bài này gom thẻ hai cấp với subtotal thật.

Ba tầng điều khiển

# 1) _order trên MODEL: thứ tự MẶC ĐỊNH của mọi search trên model này
class TheThanhVien(models.Model):
    _order = 'diem desc, name asc'   # điểm cao lên đầu, rồi tên A→Z
<!-- 2) default_order trên LIST: ghi đè _order cho riêng list này -->
<list default_order="diem desc"> ... </list>

<!-- 3) Nhóm: khai filter group_by trong SEARCH view -->
<group name="group_by">
  <filter name="gb_hang" string="Hạng thẻ" context="{'group_by':'hang_the'}"/>
  <filter name="gb_tt" string="Trạng thái" context="{'group_by':'trang_thai'}"/>
</group>

Ảnh chụp mã Python và XML nền tối, phần Python lớp TheThanhVien có _order bằng diem desc name asc là thứ tự mặc định điểm cao lên đầu rồi tên A đến Z, phần XML list có default_order diem desc ghi đè _order cho riêng list, một group name group_by chứa filter gb_hang string Hạng thẻ context group_by hang_the và filter gb_tt string Trạng thái context group_by trang_thai, chú thích user chọn 2 group liên tiếp thành nhóm nhiều cấp, một dòng gọi _read_group trả về subtotal bac 235 3 và vang 150 1, và lưu ý field không store thì không sort hoặc group được ở SQL cần store True

Hình 1: Ba tầng. _order (model) là thứ tự mặc định cho mọi truy vấn trên model — điểm giảm dần, rồi tên tăng dần. default_order (list) ghi đè _order riêng cho list đó. group_by (search) cho người dùng gom; chọn hai filter group liên tiếp → nhóm nhiều cấp. Dòng _read_group là API tính subtotal ở tầng SQL.

Xem thật: nhóm hai cấp với subtotal

Chọn nhóm "Hạng thẻ" rồi "Trạng thái", list gom hai cấp và tự cộng tổng:

Ảnh chụp giao diện list Odoo 19 thẻ thành viên đã nhóm hai cấp, ô tìm kiếm hiển thị chip Hạng thẻ mũi tên Trạng thái, nhóm cấp một Bạc có 3 thẻ tổng điểm 235 được bung ra thành ba nhóm cấp hai Đang dùng 1 thẻ 150 điểm Hết hạn 1 thẻ 85 điểm Đã khoá 1 thẻ 0 điểm, nhóm cấp một Vàng có 1 thẻ tổng 150 đang thu gọn, chân bảng tổng cộng toàn bộ 385 điểm

Hình 2: Nhóm hai cấp chạy thật. Chip "Hạng thẻ > Trạng thái" cho biết đang gom hai cấp. Nhóm cấp 1 Bạc (3): 235 bung ra thành cấp 2 theo trạng thái: Đang dùng (1): 150, Hết hạn (1): 85, Đã khoá (1): 0. Nhóm Vàng (1): 150 (thu gọn). Chân bảng tổng 385. Mỗi nhóm tự đếm số bản ghi và cộng tổng cột số có sum — không viết dòng tính nào.

Con số 235 (=150+85+0) và tổng 385 (=235+150) do Odoo tự tính bằng _read_group ở tầng SQL — nhanh kể cả trên hàng nghìn bản ghi vì database gộp hộ.

Thứ tự sắp xếp phối hợp thế nào

  • _order (model) là nền: mọi search không truyền order đều theo nó. Đặt _order cho ý nghĩa nghiệp vụ mặc định (mới nhất trước, ưu tiên cao trước).
  • default_order (list) ghi đè _order chỉ cho list đó — hữu ích khi một màn hình cần thứ tự khác mặc định.
  • Người dùng bấm tiêu đề cột để sort tạm — ghi đè cả hai, chỉ trong phiên xem.
  • Trong mỗi nhóm, các bản ghi vẫn sắp theo _order/default_order.

Ba tầng theo thứ tự ưu tiên: cột người dùng bấm > default_order > _order.

Cạm bẫy: field không store không sort/group được

Đây là giới hạn quan trọng. Sắp xếp và nhóm chạy ở tầng SQL (database). Một field computed không store=True chỉ tồn tại trong bộ nhớ Python, không có cột trong bảng — nên database không sort/group theo nó được. Nếu bạn cần sort/group theo một computed field, phải khai store=True để nó có cột thật.

Vài lưu ý Odoo 19

  • _order dùng tên cột SQL — field quan hệ sort theo id trừ khi field đích có _order phù hợp; sort theo field của bản ghi liên kết cần cú pháp partner_id.name (giới hạn).
  • Nhóm nhiều cấp: chọn các group_by theo thứ tự bạn muốn lồng; bỏ chọn để tháo cấp.
  • Subtotal chỉ hiện cho cột có sum/avg trong list (bài list view); nhóm không tự cộng cột không khai.
  • _read_group (Odoo 19; trước là read_group) là API đọc dữ liệu đã gom — dùng khi cần số liệu nhóm trong code.

Ba ý mang về

  1. Ba tầng thứ tự: _order (model, mặc định toàn hệ thống) < default_order (list, ghi đè cho list đó) < cột người dùng bấm (tạm thời). Trong mỗi nhóm vẫn sắp theo order.
  2. Nhóm nhiều cấp qua group_by của search: chọn 2 filter group liên tiếp → lồng (Hạng thẻ > Trạng thái); mỗi nhóm tự đếm và cộng subtotal cột có sum. Đã chứng minh: Bạc 235 (Đang dùng 150 / Hết hạn 85 / Đã khoá 0), Vàng 150, tổng 385.
  3. Sắp/nhóm chạy ở SQL → field computed phải store=True mới sort/group được; dùng _read_group để lấy số liệu nhóm trong code.

Nói tới list, còn một chế độ khiến bảng thành nơi nhập liệu chứ không chỉ xem: Phần sau mổ xẻ editable list — cho sửa và thêm dòng ngay trên bảng (editable="top"/"bottom"), tiện cho dòng chi tiết mà không cần mở form.