# TSHIRTORDER-1642 — PINshirt: thêm 2 cột mới trong CSV (`model_sku`, `model_name`)

## Yêu cầu gốc

> TSHIRTORDER-1642 Pinshirt integration add 2 new column in CSV

Sếp bổ sung:

> add per producst row, model sku and model color , we use the name

File CSV mẫu mới: xem `files/pinshirt_order_10005.csv`.

---

## Tổng quan

PINshirt thêm 2 cột vào cuối mỗi dòng của CSV nhập đơn: `model_sku` và `model_name`, tức mã và tên của **mẫu áo phôi** (blank garment, Stanley/Stella). Đây là tiếp nối của **TSHIRTORDER-1627** (import PINshirt, doc `docs/issues/TSHIRTORDER-1627-pinshirt-import/pinshirt-import.md`).

Thay đổi chỉ ở **backend**: lưu giá trị 2 cột mới cho **từng dòng sản phẩm** của order (`orders_products`) vào 2 field mới `modelSku` và `modelName`. Hai field này được trả ra trong API chi tiết order và sửa được qua API cập nhật item. Hiển thị trên màn hình order cần làm ở **repo React (repo khác)**.

So sánh CSV cũ và mới (chỉ khác ở 2 cột cuối):

| Cột mới | Ví dụ |
|---|---|
| `model_sku` | `STTU169`, `STSU168`, `STAU760` |
| `model_name` | `Stanley/Stella Creator 2.0`, `Stanley/Stella Drummer 2.0`, `Stanley/Stella Woven Tote Bag` |

### ⚠️ Phát hiện quan trọng: code hiện tại không lỗi với CSV mới

| Hạng mục | File | Trạng thái |
|---|---|---|
| Đọc CSV theo **tên cột**, cột thừa bị bỏ qua (chỉ kiểm tra thiếu cột bắt buộc) | `src/Service/FileService.php` — `importOrderPinshirtReadFileContent()` | ✅ CSV mới vẫn import được ngay cả khi chưa sửa, chỉ là 2 cột mới bị bỏ qua |
| Field theo từng dòng `supplierSku` / `supplierColor` (cột export "Leverantör SKU/Färg") | `src/Entity/OrderProduct.php`, từ TSHIRTORDER-1616 | ✅ Đã có, nhưng **không dùng**: đã chốt tạo field riêng (xem Questions #1) |
| Field `modelSku` / `modelName` trên `OrderProduct` | — | ✅ Đã làm trong ticket này |

---

## Luồng hoạt động

Luồng import không đổi so với 1627 (cron `order:import_pinshirt` → đọc CSV → gom theo `order_id` → `PinshirtOrderImporter::import()`). Ticket này chỉ thêm phần mapping cho mỗi dòng item:

| Cột CSV | Cột DB (`OrderProduct::$…`) | Ghi chú |
|---|---|---|
| `model_sku` | `orders_products.model_sku` (`modelSku`) | Lưu nguyên giá trị, chỉ ghi khi khác rỗng |
| `model_name` | `orders_products.model_name` (`modelName`) | Lưu nguyên giá trị, chỉ ghi khi khác rỗng |

Các cột khác (`color` → `product_color`, print info → `comment`...) giữ nguyên như 1627.

**Product không đổi:** SKU của product vẫn là `print_id` (quyết định #22 của 1627). `model_sku` **không** dùng để tìm hay tạo product.

**2 cột mới là không bắt buộc:** file theo định dạng cũ (không có `model_sku`/`model_name`) vẫn import bình thường, 2 field để trống. Không bắt buộc để tránh file cũ bị coi là lỗi cấp file và bị giữ lại trong `new/`.

---

## Questions / Đã xác nhận

| # | Câu hỏi | Trả lời | Ghi chú kỹ thuật |
|---|---------|---------|-------------------|
| 1 | Lưu 2 cột mới ở đâu: dùng lại `supplierSku`/`supplierColor` (TSHIRTORDER-1616) hay tạo field mới? | **Tạo 2 field mới trên `OrderProduct` để lưu đúng giá trị 2 cột mới trong CSV** (Kevin, 06/10/2026) | `supplierSku`/`supplierColor` giữ nguyên cho mục đích cũ (người dùng nhập tay) |
| 2 | Tên field thứ 2? | **`modelName`** | Sếp viết "model color" nhưng cột thứ 2 của CSV là `model_name` (vd `Stanley/Stella Creator 2.0`), không phải màu, nên đặt tên field theo cột CSV |
| 3 | Có đổi SKU của product từ `print_id` sang `model_sku` không? | **Không**, chỉ lưu thêm thông tin theo từng dòng | Logic tìm/tạo product của 1627 giữ nguyên |

---

## Thiết kế kỹ thuật

### Entity & Migration

| Field (`OrderProduct`) | Cột DB | Kiểu | Ý nghĩa |
|---|---|---|---|
| `modelSku` | `orders_products.model_sku` | `varchar(255)`, nullable | SKU mẫu áo phôi của dòng (PINshirt `model_sku`) |
| `modelName` | `orders_products.model_name` | `varchar(255)`, nullable | Tên mẫu áo phôi của dòng (PINshirt `model_name`) |

Migration: `migrations/Version20261006090000.php`, **viết tay** (không dùng `doctrine:migrations:diff`, xem `Version20260924090615`). Đã chạy trên DB local (06/10/2026).

### API (cho FE)

- **Đọc:** `GET /api/v1/orders/{id}` và các API trả order kèm items: mỗi phần tử `items[]` có thêm `modelSku`, `modelName` (string hoặc `null`). Nguồn: `OrderProductRepository::getByOrderId()`. 2 field cũng có trong `OrderProductRepository::query()` (danh sách order product).
- **Ghi khi tạo/sửa order:** `POST /api/v1/orders/` và `update`: mỗi item trong `items[]` nhận thêm `modelSku`, `modelName` (không bắt buộc).
- **Sửa riêng 1 item:** `POST /api/v1/orders/{orderId}/item/{orderProductId}` với body `{"modelSku": "...", "modelName": "..."}`, gửi `null` để xoá.

#### ⚠️ Lưu ý bẫy quan trọng

- `addProducts()` chỉ set `modelSku`/`modelName` khi **có key** trong item (`array_key_exists`), giống `supplierSku`. FE sửa order mà không gửi 2 key này thì giá trị cũ được giữ nguyên, không bị ghi đè rỗng.
- Import PINshirt với order **đã tồn tại** (thay item kiểu Blavitt, xem 1627) sẽ tạo lại item, nên `modelSku`/`modelName` lấy theo file mới. Dòng nào file mới để trống thì thành `null`.

---

## TODO List

### Backend — Entity & Migration
- [x] `src/Entity/OrderProduct.php`: thêm `$modelSku`, `$modelName` (`string(255)`, nullable) + getter/setter
- [x] `migrations/Version20261006090000.php`: `ALTER TABLE orders_products ADD model_sku / model_name VARCHAR(255)`, có `down()`
- [x] Chạy migration trên local
- [ ] Chạy `doctrine:migrations:migrate` trên DEV, rồi PROD, **trước** khi deploy code

### Backend — Service / Repository
- [x] `OrderService::addProducts()`: đọc `$item['modelSku']`, `$item['modelName']`, set khi có key
- [x] `OrderService::orderProductUpdate()`: cho phép sửa `modelSku`, `modelName`
- [x] `OrderProductRepository::query()` và `getByOrderId()`: select thêm `modelSku`, `modelName`
- [x] `FileService`: hằng số `PINSHIRT_OPTIONAL_ITEM_COLUMNS = ['model_sku', 'model_name']`; thiếu cột thì giá trị `''`
- [x] `PinshirtOrderImporter::buildItems()`: `modelSku` = `model_sku`, `modelName` = `model_name`

### Frontend — React *(repo khác)*
- [ ] Hiển thị `modelSku`, `modelName` trên dòng sản phẩm của order (vị trí/nhãn: chờ sếp)
- [ ] (Tuỳ chọn) cho sửa qua `POST /orders/{orderId}/item/{orderProductId}`

### Test / kiểm tra
- [x] `order:import_pinshirt --local-dir=... --no-archive` với CSV mới (order test `91642`): 3 dòng có `model_sku` `STTU169`/`STSU168`/`STAU760`, `model_name` `Stanley/Stella Creator 2.0`/`Stanley/Stella Drummer 2.0`/`Stanley/Stella Woven Tote Bag`
- [x] Cùng lệnh với CSV định dạng cũ (order test `91643`): vẫn được tạo, `model_sku` = `model_name` = `null`
- [ ] Xoá 2 order test `91642`, `91643` trên DB local
- [ ] Test trên DEV với file thật của PINshirt

---

## Các file/files liên quan

| File | Mục đích |
|------|----------|
| `src/Entity/OrderProduct.php` | 2 field mới `modelSku`, `modelName` |
| `migrations/Version20261006090000.php` | Thêm 2 cột `orders_products.model_sku`, `model_name` |
| `src/Service/OrderService.php` | `addProducts()`, `orderProductUpdate()` |
| `src/Repository/OrderProductRepository.php` | `query()`, `getByOrderId()` trả thêm 2 field |
| `src/Service/FileService.php` | `PINSHIRT_OPTIONAL_ITEM_COLUMNS`, `importOrderPinshirtReadFileContent()` |
| `src/Service/Pinshirt/PinshirtOrderImporter.php` | `buildItems()` |
| `docs/issues/TSHIRTORDER-1627-pinshirt-import/pinshirt-import.md` | Doc gốc của import PINshirt |
| `files/pinshirt_order_10005.csv` | CSV mẫu có 2 cột mới |
