# TSHIRTORDER-1645 — BUG: Credit (return) order sync to Woo hoàn tiền toàn bộ sản phẩm

## Yêu cầu gốc

> TSHIRTORDER-1645 BUG - credit order sync to woo
> if create new credit order and it sync to woo , we see that if only credit one producst in email its created all producst - see images
>
> check if bug on order or needs to adjust plugin for it

Kèm ảnh mockup (xem `files/`):
- `files/Skärmavbild 2026-10-06 kl. 11.27.11.png` — email Woo "Beställning återbetald #15636" liệt kê nhiều sản phẩm.
- `files/unnamed-3.png` — Returorder #R3078 trong hệ thống, chỉ có 1 dòng (SKU `1915186-999000-5`, qty 1, 800 kr).
- `files/unnamed-4.png` — đơn gốc 15636 có nhiều dòng (vest, pants, t-shirt, frakt).

---

## Tổng quan

Khi nhân viên bấm Refund trên ReturnOrder của đơn `createdFrom = wordpress`, `ReturnOrderService::refund()` gọi webhook `resync_url_integration` của plugin Woo. Return order chỉ có 1 dòng nhưng Woo hoàn và gửi email cho cả đơn. Phạm vi: backend (repo này) + plugin Woo (repo khác).

**Kết luận: nguyên nhân nằm ở source của mình.** Payload gửi sang plugin chỉ có `order_id`, `sync_id`, `resync_type = refund_order`, không có danh sách dòng cần hoàn, nên plugin chỉ có thể hoàn toàn bộ đơn.

> ⚠️ Chưa kiểm tra code plugin Woo (không nằm trong repo). Phần "plugin hoàn toàn bộ khi thiếu `refund_items`" là suy ra từ payload + mockup.

---

## Luồng hoạt động

1. UI bấm Refund → `ReturnOrderController` → `ReturnOrderService::refund($id)`.
2. Nếu đơn không phải `wordpress` → chỉ đổi status Refunded (không đổi).
3. Nếu `wordpress` → POST JSON tới `Order::getResyncUrlIntegration()`.
4. HTTP 200 → set status Refunded + `refundDate`.

---

## API contract / Thiết kế kỹ thuật

Payload mới (POST tới plugin Woo):

```json
{
  "order_id": "15636",
  "sync_id": "md5(synId__orderNr)",
  "resync_type": "refund_order",
  "refund_items": [
    {
      "item_id": "987",
      "order_item_id": 123,
      "product_id": 45,
      "sku": "1915186-999000-5",
      "size": "M",
      "quantity": 1,
      "price": 800.0
    }
  ],
  "refund_shipping": 63.2,
  "refund_total": 863.2
}
```

| Field | Ý nghĩa |
|-------|---------|
| `refund_items[]` | Chỉ các dòng của ReturnOrder (qty > 0, chưa xoá). Plugin tạo `wc_create_refund` với `line_items` tương ứng |
| `item_id` | **Woo line item id** (OrderProduct.itemId, lưu lúc import từ Woo) — dùng cái này để match dòng; `sku`/`size` chỉ để dự phòng. Có thể null với dòng thêm tay |
| `order_item_id` | ID OrderProduct phía mình (không phải Woo line item id) |
| `refund_shipping` | `ReturnOrder.shippingFee` — **chưa gồm VAT** |
| `refund_total` | `ReturnOrder.totalSum`, dùng để đối chiếu tổng |

#### ⚠️ Lưu ý bẫy quan trọng
- Tương thích ngược: plugin cũ bỏ qua field lạ nên vẫn hoàn toàn bộ cho tới khi plugin được cập nhật. Plugin mới: nếu không có `refund_items` thì giữ hành vi cũ.
- **Giá chưa gồm VAT**: `price`, `refund_shipping`, `refund_total` là ex VAT (mockup R3078: 800 + 63.2 = 863.20, incl. 1079). Woo lưu giá incl. VAT (1000/79) → plugin nên refund theo `quantity` của line (Woo tự tính tiền) thay vì dùng `price`, và quyết định cách xử lý shipping.
- Dòng ReturnOrder thêm tay không phải từ Woo import có `item_id` null.
- Đã sửa kèm lỗi precedence ở message lỗi (`'Body data error - ' . isset(...) ? ... : ''` luôn trả về message sai).

---

## TODO List

### Backend — Service
- [x] `src/Service/ReturnOrderService.php`: thêm `buildRefundItems()` và gửi `refund_items` (kèm `item_id` = Woo line item id), `refund_shipping`, `refund_total` trong `refund()`.
- [x] Sửa lỗi precedence ở message `Body data error`.

### Plugin Woo (repo khác)
- [ ] Đọc `refund_items` (match theo `item_id`), `refund_shipping`, tạo partial refund theo từng line thay vì refund cả đơn.
- [ ] Giữ hành vi cũ nếu không có `refund_items`.

### Test / kiểm tra
- [ ] Đơn Woo nhiều dòng, return 1 dòng → email Woo chỉ có 1 dòng.
- [ ] Return nhiều dòng / qty một phần.
- [ ] Đơn không phải wordpress: hành vi không đổi.

---

## Các file liên quan

| File | Mục đích |
|------|----------|
| `src/Service/ReturnOrderService.php` | `refund()`, `buildRefundItems()` |
| `src/Entity/ReturnOrderItem.php` | Nguồn dữ liệu dòng hoàn |

---

## Pending Clarification

- Có hoàn phí vận chuyển (`refund_shipping`) về Woo không? Phí ship Woo thu là 79 incl. VAT, return order ghi 63,2 ex VAT.
- `ReturnOrderService::refund()` không kiểm tra return order đã Refunded chưa → bấm lần 2 sẽ gọi webhook lần nữa (Woo có thể hoàn lặp). Có từ trước, chưa sửa trong ticket này.
