# TSHIRTORDER-1594 — Dynamic Product Variants

## Yêu cầu gốc

> Customer wants a new product type in tshirt, same as Shopify with dynamic attributes. Instead of using sub-products for size as we do today.
>
> In Shopify you can create attribute list e.g. Color, Print — and then in product select all you want to add and it automatically adds a number after in SKU.

---

## Tổng quan

Thêm một **product type mới** (`dynamic`) song song với hệ thống sub-product hiện tại (`classic`). User định nghĩa các **Attribute** toàn cục (Color, Size, Print...), mỗi attribute có các **AttributeValue** với code số. Khi tạo product, user chọn attributes + values → hệ thống **tự động generate** toàn bộ variant combinations với SKU tính tự động.

Cả hai loại sản phẩm tồn tại đồng thời, không ảnh hưởng nhau.

---

## Data Model

### Entities và quan hệ

```
Attribute (global)
  ├── name: "Color"
  ├── code: "01"           ← dùng để tổ chức, KHÔNG xuất hiện trong SKU
  └── values:
        ├── AttributeValue: label="Red",   code="001"
        ├── AttributeValue: label="Blue",  code="002"
        └── AttributeValue: label="Green", code="003"

Attribute (global)
  ├── name: "Size"
  ├── code: "02"
  └── values:
        ├── AttributeValue: label="S",  code="001"
        ├── AttributeValue: label="L",  code="002"
        └── AttributeValue: label="XL", code="003"

Product
  ├── name: "Classic T-Shirt"
  ├── base_sku: "SHIRT"
  ├── attributes: [Color(01) → [Red,Blue], Size(02) → [S,L]]
  └── variants: auto-generated (4 variants = 2×2)

Variant (auto-generated)
  ├── combination: "Red / Small"
  ├── full_sku: "SHIRT-001-001"   ← computed, never entered manually
  ├── price: editable after generation
  └── stock: editable after generation
```

### Bảng tóm tắt

| Entity | Key fields | Who creates it |
|--------|-----------|----------------|
| `Attribute` | name, code | Admin, thủ công |
| `AttributeValue` | label, code, attribute_id | Admin, thủ công |
| `Product` | name, base_sku, attribute selection | User, thủ công |
| `Variant` | product_id, value combination, full_sku | System, tự động |

**Attribute là global** — được tạo một lần bởi admin và dùng lại cho mọi sản phẩm.

---

## SKU Generation Formula

Các segment nối nhau bằng dấu `-`. Thứ tự theo thứ tự attribute được chọn trên product.

```
BASE - {value_code_attr1} - {value_code_attr2}
```

Ví dụ với base SKU `SHIRT`, Color(01) + Size(02):

```
SHIRT + Color Red(001)  + Size S(001)  →  SHIRT-001-001
SHIRT + Color Red(001)  + Size L(002)  →  SHIRT-001-002
SHIRT + Color Blue(002) + Size S(001)  →  SHIRT-002-001
SHIRT + Color Blue(002) + Size L(002)  →  SHIRT-002-002
```

**Chỉ value code xuất hiện trong SKU — attribute code (01, 02...) là metadata, không vào SKU string.**

---

## Business Rules

1. **SKU uniqueness** — Base SKU + value code combination phải unique trên toàn catalog. System validate khi save.

2. **Code immutability** — Code của Attribute và AttributeValue **không được thay đổi** sau khi đã có product nào sử dụng. Đổi code sẽ silently break toàn bộ SKU hiện có.

3. **Attribute order matters** — Thứ tự chọn attributes trên product quyết định thứ tự segment trong SKU. Reorder attributes → regenerate toàn bộ variant SKUs.

4. **Full cartesian product** — Hệ thống luôn generate đầy đủ tổ hợp. Không thể bỏ qua combination riêng lẻ khi generate — chỉ có thể **deactivate** sau khi đã generate.

5. **Adding values later** — Nếu thêm value mới vào một Attribute đang được dùng bởi các products, hệ thống tự động generate thêm variants mới cho toàn bộ products đó.

---

## User Flows

### Flow 1 — Tạo Attribute (Admin)

| Step | Action |
|------|--------|
| 01 | Đặt tên attribute, vd: "Color" |
| 02 | Set attribute code, vd: "01" |
| 03 | Thêm values: mỗi value có label + code riêng |
| 04 | Save — attribute được lưu globally, reusable |

### Flow 2 — Tạo Product với variants (User)

| Step | Action |
|------|--------|
| 01 | Nhập product info (name, description...) |
| 02 | Nhập base SKU, vd: "SHIRT" |
| 03 | Chọn attributes và sắp xếp thứ tự |
| 04 | Pick values cho từng attribute |
| 05 | Review bảng variants được auto-generate, chỉnh price/stock |

---

## UI Screens (theo mockup khách hàng)

**Panel trái — New attribute:**
- Attribute name input
- Attribute code input (numeric string)
- Values list với chip mỗi value (code + label), nút "+ Add value"

**Panel phải — New product / attributes:**
- Base SKU input
- Mỗi attribute hiển thị dạng chip list để pick values

**Bảng Generated variants preview:**

| Color | Size | Full SKU | Price | Stock |
|-------|------|----------|-------|-------|
| Red | S | SHIRT-001-001 | 29.00 | 100 |
| Red | L | SHIRT-001-002 | 29.00 | 100 |
| Blue | S | SHIRT-002-001 | 29.00 | 100 |
| Blue | L | SHIRT-002-002 | 29.00 | 100 |

Price và Stock là **bắt buộc** — user phải nhập cho từng variant sau khi generate. SKU được compute tự động, không cho sửa.

---

## Đề xuất DB Schema (mới)

```sql
-- Global attribute definitions
product_attributes        (id, name, code, date_created, date_updated)
product_attribute_values  (id, attribute_id, label, code, sort_order, date_created, date_updated)

-- Per-product attribute selection + ordering
product_variant_attributes        (id, product_id, attribute_id, sort_order)
product_variant_attribute_values  (id, product_variant_attribute_id, attribute_value_id)

-- Generated combinations
product_variants              (id, product_id, full_sku, price, stock, is_active, date_created, date_updated)
product_variant_combinations  (id, variant_id, attribute_value_id)
```

Field `variantType` thêm vào `Product`: `classic` (default) | `dynamic`.

---

## Tác động lên code hiện tại

| Phần | Mức độ ảnh hưởng |
|------|-----------------|
| `src/Entity/Product.php` | Thêm field `variantType` — không đổi gì khác |
| Sub-product flow hiện tại | Không thay đổi |
| `OrderProduct` | Cần review — khi order cần chọn variant thay vì sub-product |
| Shopify sync | Cần spec riêng nếu cần sync variants |
| Ecom (webshop) | Attribute selector UI thay cho size list |

---

## Scope làm việc

> **Frontend React admin**: dev React làm — không tính vào estimate.
> **Trong scope (bắt buộc)**: API backend + DB + Ecom webshop (Twig) — fix toàn bộ source.

---

## Estimate

### Phase 1 — Global Attribute CRUD (API only)

| Task | Hours |
|------|:---:|
| Entity `ProductAttribute` + `ProductAttributeValue` + migrations | 4h |
| `ProductAttributeService` + API endpoints (`/api/v1/product-attributes/`) | 4h |
| **Subtotal** | **8h** |

### Phase 2 — Variant engine (API only)

| Task | Hours |
|------|:---:|
| 4 entities mới + migrations (`ProductVariantAttribute`, `ProductVariantAttributeValue`, `ProductVariant`, `ProductVariantCombination`) | 4h |
| `ProductVariantService`: cartesian product, SKU formula, business rules | 8h |
| API endpoints variants (`/api/v1/products/{id}/variants/`) | 4h |
| Update `ProductService::generateItemDetail` + `variantType` field | 4h |
| **Subtotal** | **20h** |

### Phase 3 — Order flow (API only)

| Task | Hours |
|------|:---:|
| `OrderProduct`: thêm `variantId` + `variantSku` (song song `subProductId` hiện tại) | 4h |
| **Subtotal** | **4h** |

### Phase 4 — Ecom webshop (Twig) — bắt buộc

| Task | Hours |
|------|:---:|
| Attribute selector UI trên product page thay cho size list | 8h |
| **Subtotal** | **8h** |

### Tổng

| Scope | Hours |
|-------|:---:|
| Phase 1–3 (API + DB) | **32h (~4 ngày)** |
| Phase 4 (ecom webshop) | **8h (~1 ngày)** |
| **Total** | **~40h (~5 ngày)** |

---

## Estimate — React Dev (frontend admin)

### Phase 1 — Attribute management (CRUD)

| Task | Hours |
|------|:---:|
| List attributes page | 3h |
| Create/edit attribute + quản lý values (add/remove/reorder) | 5h |
| **Subtotal** | **8h** |

### Phase 2 — Product form (dynamic type)

| Task | Hours |
|------|:---:|
| Toggle classic/dynamic | 1h |
| Attribute picker + ordering (drag/drop thứ tự) | 5h |
| Value picker per attribute | 3h |
| Variants table: generate preview, edit price/stock, SKU readonly | 6h |
| **Subtotal** | **15h** |

### Phase 3 — Order flow

| Task | Hours |
|------|:---:|
| Variant selector khi add product vào order | 5h |
| Hiển thị variant info trong order line items | 3h |
| **Subtotal** | **8h** |

### Tổng React dev

| Hours |
|:---:|
| **~31h (~4 ngày)** |

> **Lưu ý:** Phase 2 nặng nhất — drag/drop ordering + variants table inline editing. Nếu project đã có component library sẵn (table, drag/drop) thì giảm được kha khá.

---

## Files đính kèm

Xem `files/` — mockup UI + data model từ khách hàng (screenshots + docx).