Bạn muốn dời field "Hạng thẻ" từ cột trái sang cột phải của form — chỉ đổi vị trí, không đổi gì khác. Trực giác đầu tiên là "thêm nó vào chỗ mới". Nhưng làm vậy sẽ có hai field cùng tên hang_the trong view — và Odoo báo lỗi trùng. Cách đúng là dùng position="move": nó di chuyển field có sẵn, không tạo bản sao. Cùng họ với nó là position="replace" để thay hoặc xoá field. Bài này dời một field thật để thấy move hoạt động.
move và replace
<!-- MOVE: chuyển field CÓ SẴN sang chỗ khác (KHÔNG nhân đôi) -->
<!-- Đặt <field position="move"/> BÊN TRONG một xpath ĐÍCH -->
<xpath expr="//field[@name='diem']" position="after">
<field name="hang_the" position="move"/> <!-- chuyển hang_the tới sau diem -->
</xpath>
<!-- REPLACE: thay field bằng nội dung MỚI -->
<field name="diem" position="replace">
<field name="diem" widget="progressbar" string="Tiến độ điểm"/>
</field>
<!-- REPLACE rỗng = XOÁ field khỏi view -->
<field name="tai_lieu" position="replace"/>

Hình 1: Hai kỹ thuật. position="move" phải đặt bên trong một xpath đích (một <field> hay <xpath> khác dùng after/before/inside): nó nhấc field có sẵn tới đó. position="replace" thay field bằng nội dung con (ví dụ cùng field nhưng thêm widget); replace rỗng thì xoá field khỏi view. Ghi nhớ cuối: không thể có hai field cùng name trong một view.
Xem thật: field được dời sang cột khác
Mình viết một extension dời hang_the (Hạng thẻ) từ cột trái sang cột phải, ngay sau diem. Mở form thẻ VIP-0003:

Hình 2: position="move" chạy thật. Field "Hạng thẻ" giờ nằm ở cột phải ("Điểm và số dư"), ngay dưới "Điểm tích luỹ" — trước đó nó ở cột trái ("Thông tin thẻ"). Cột trái không còn "Hạng thẻ". Chỉ một field hang_the tồn tại, đã đổi chỗ — không nhân đôi. So với việc chép field (gây lỗi trùng), move là cách sạch để bố trí lại.
move vs replace: chọn đúng
move: giữ field, chỉ đổi vị trí. Dùng khi muốn sắp xếp lại bố cục mà không đổi cách field hoạt động. Field vẫn là một, ở chỗ mới.replace(có nội dung): bỏ field cũ, đặt nội dung mới vào đúng chỗ đó. Dùng khi muốn đổi field — thêm widget, đổi label, thêm điều kiện. Thường lặp lại chính field với thuộc tính mới.replace(rỗng): xoá field khỏi view (chỉ khỏi view, không xoá dữ liệu).
Cạm bẫy field trùng tên
Đây là lỗi kinh điển. Trong một view, mỗi field chỉ được xuất hiện một lần theo name. Nếu bạn muốn:
- Dời field: dùng
move(một field, chỗ mới). ✅ - Hiện field ở hai chỗ: không được chép field. Cách vòng: để field gốc
invisible="1"(nạp dữ liệu) và thêm một field khác tên hiển thị, hoặc thiết kế lại. Thường không nên — hai ô cùng field dễ rối. - Đổi field:
replace(bỏ cũ, đặt mới cùng tên vào chỗ đó — hợp lệ vì cái cũ đã bị bỏ).
Vài lưu ý Odoo 19
movephải nằm trong một node đích (position="after"/"before"/"inside"của một xpath khác).<field name="x" position="move"/>đứng một mình không có ý nghĩa.replacecẩn thận khi nhiều module cùng đụng field — module Areplacefield mà module B trông đợi nó còn nguyên thì B vỡ. Ưu tiênattributes(đổi thuộc tính) nếu chỉ cần chỉnh nhẹ.movekhông kéo theo nhãn tự động sai — Odoo giữ label của field khi di chuyển.- Dùng
moveđể sắp xếp lại thứ tự trong cùng một group cũng được (move một field ra trước/sau một field khác).
Ba ý mang về
position="move"di chuyển một field CÓ SẴN sang chỗ khác (đặt trong một xpath đích), không nhân đôi — đã chứng minh: "Hạng thẻ" chuyển từ cột trái sang cột phải, chỉ một field tồn tại.position="replace"thay field bằng nội dung mới (thường cùng field + widget/thuộc tính khác);replacerỗng thì xoá field khỏi view (không xoá dữ liệu).- Không được có hai field cùng
nametrong một view: dời thì dùngmove, đổi thì dùngreplace; muốn hai chỗ phải để field gốcinvisible+ thêm field khác tên.movecần nằm trong node đích;replacethận trọng khi module khác cùng đụng.
Ta đã thành thạo sửa view của app khác. Lần sau áp dụng để thêm hành động: Phần sau hướng dẫn thêm một nút vào form của app khác (ví dụ nút "Tạo thẻ thành viên" ngay trên form Liên hệ) — kết hợp view inherit với nút gọi method, một tuỳ biến rất hay dùng trong thực chiến.