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"/>

Ảnh chụp mã XML nền tối, phần MOVE dùng một xpath đích trỏ tới sau field diem và bên trong đặt field hang_the với position move để chuyển hang_the tới sau diem không nhân đôi, phần REPLACE thay field diem bằng field diem có widget progressbar và string Tiến độ điểm, phần REPLACE rỗng với field tai_lieu position replace nghĩa là xoá field khỏi view, cuối cùng phần phân biệt ghi move giữ field chỉ đổi vị trí, replace bỏ field cũ đặt nội dung mới, và muốn thêm bản sao field không được vì field trùng name sẽ lỗi nên phải dùng move hoặc invisible field gốc thêm field mới tên khác

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:

Ảnh chụp form Odoo thẻ VIP-0003 sau khi di chuyển field, cột trái Thông tin thẻ giờ chỉ còn Khách hàng, Ghi chú nội bộ và Ngày cấp, KHÔNG còn Hạng thẻ, cột phải Điểm và số dư hiển thị Điểm tích luỹ 150 phần trăm rồi Hạng thẻ Vàng ngay bên dưới rồi Số dư 0 đô la Tiền tệ USD Ưu đãi áp dụng, cho thấy field Hạng thẻ đã được chuyển từ cột trái sang cột phải

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

  • move phả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.
  • replace cẩn thận khi nhiều module cùng đụng field — module A replace field mà module B trông đợi nó còn nguyên thì B vỡ. Ưu tiên attributes (đổi thuộc tính) nếu chỉ cần chỉnh nhẹ.
  • move khô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ề

  1. 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.
  2. position="replace" thay field bằng nội dung mới (thường cùng field + widget/thuộc tính khác); replace rỗng thì xoá field khỏi view (không xoá dữ liệu).
  3. Không được có hai field cùng name trong một view: dời thì dùng move, đổi thì dùng replace; muốn hai chỗ phải để field gốc invisible + thêm field khác tên. move cần nằm trong node đích; replace thậ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.