Wizard một bước thì đơn giản: điền, bấm, xong. Nhưng nhiều tác vụ cần dẫn người dùng qua từng màn hình — chọn ở bước 1, xem lại và xác nhận ở bước 2. Odoo không có "wizard nhiều trang" dựng sẵn; thay vào đó có một mẫu rất gọn: một field state quyết định đang ở bước nào, view ẩn/hiện nhóm theo state, và một mẹo khiến method mở lại chính wizard để đi bước tiếp mà không mất dữ liệu. Bài này biến wizard cộng điểm thành hai bước.
state + mở lại chính mình
Thêm một field state (Selection các bước). Nút "Tiếp" gọi method đổi state rồi trả về một action act_window mở lại chính bản ghi wizard này (res_id=self.id):

Hình 1: state mặc định 'chon'. action_next ghi state sang 'xac_nhan' rồi _reopen() — mở lại act_window với res_id=self.id. Vì là cùng một bản ghi, mọi dữ liệu đã nhập vẫn còn. View dùng <group invisible="state != '...'"> để hiện đúng nhóm của bước, và nút cũng invisible theo state.
Mấu chốt của mẹo: bước tiếp không tạo wizard mới, mà mở lại wizard cũ đã đổi state. Nhờ res_id=self.id, người dùng thấy đúng dữ liệu họ vừa nhập, chỉ khác là màn hình chuyển sang nhóm của bước mới.
Bước 1 — Chọn
Mở wizard, ta thấy bước 1: chọn thẻ và số điểm:

Hình 2: Bước 1 — nhóm "Chọn thẻ và số điểm" hiện ra vì state='chon'. Chỉ có nút "Tiếp →" và "Huỷ" (các nút của bước 2 đang bị invisible). Người dùng chọn thẻ, đặt số điểm, rồi bấm "Tiếp →".
Bước 2 — Xác nhận
Bấm "Tiếp →", state thành 'xac_nhan', wizard mở lại và hiện nhóm của bước 2 — một bản tóm tắt để duyệt trước khi chạy:

Hình 3: Bước 2 — nhóm "Xác nhận" hiện ra (state đổi). Khối tóm tắt cho biết sẽ cộng bao nhiêu điểm cho thẻ nào — chính là dữ liệu từ bước 1, còn nguyên nhờ mở lại cùng res_id. Nút giờ là "← Quay lại" (về bước 1) và "Xác nhận cộng điểm" (chạy thật). Bấm Quay lại là action_back ghi state về 'chon'.
Hai cách dựng wizard nhiều bước
- Một TransientModel, nhiều state (cách ở đây): gọn, mọi bước dùng chung dữ liệu. Hợp cho luồng tuyến tính vài bước. Nút mở lại chính wizard bằng
res_id=self.id. - Mỗi bước một TransientModel riêng: bước trước lưu id (hoặc truyền qua context), method trả
act_windowmở wizard của bước sau. Hợp khi các bước khác nhau nhiều về dữ liệu, hoặc muốn tách bạch.
Với đa số trường hợp, mẫu state một model là đủ và dễ đọc nhất.
Vài lưu ý
invisible="state != '...'"viết thẳng biểu thức (Odoo 19) — không bọcattrs.- Dữ liệu còn vì cùng bản ghi. Nếu lỡ tạo wizard mới ở mỗi bước, dữ liệu nhập trước sẽ mất — nhớ
res_id=self.id. - Đặt
statemặc định cho bước đầu, nếu không wizard mở ra trống trơn (không nhóm nào hiện). - Có thể thêm bước kiểm tra: ở
action_next, validate dữ liệu trước khi cho sang bước sau; sai thìraise UserError.
Ba ý mang về
- Wizard nhiều bước = một field
state+ view ẩn/hiện nhóm theo state; mỗi bước một<group invisible="state != '...'">, nút cũnginvisibletheo state. - Method đổi bước ghi
staterồi trảact_windowmở lại chính wizard (res_id=self.id) — dữ liệu người dùng nhập vẫn còn vì là cùng một bản ghi. - Hai cách dựng: một TransientModel nhiều state (gọn, chung dữ liệu) hay mỗi bước một TransientModel (tách bạch); nhớ đặt
statemặc định và validate ở bước chuyển.
Wizard đến giờ đều tự đóng hoặc mở lại chính nó. Nhưng method wizard trả được bất kỳ action nào — kể cả mở một màn hình khác. Bài sau: Phần sau về wizard trả về action tiếp theo — chạy xong rồi đưa người dùng thẳng tới danh sách kết quả, một báo cáo, hay một bản ghi vừa tạo.