# TSHIRTORDER-1548 — Change stock sync direction: Order → WooCommerce

## Yêu cầu gốc

> **TSHIRTORDER-1548 CHANGE STOCK sync from Order to woo**
> Today we sync stock from woo to order we want to change it
>
> **Order changes**
> 1. Create sync to woo from order — sync stock, sku, last datetime changes
> 2. Add in channel sync stock to woo YES / NO
> 3. Select interval in channel: once a day (nighttime) / 2 times a day (night and day) / 3 times a day (nighttime, at 11 and at 15)
>
> **WOO plugin**
> 1. Remove stock export
> 2. Import stock from file from Order

---

## Tổng quan

Trước đây stock được sync theo chiều **WooCommerce → Symfony** (WP plugin gọi API `/import-wp-stock`).
Sau task này, chiều sync đảo ngược: **Symfony (TshirtOrder) → WooCommerce** (qua file JSON trên FTP).

---

## Thay đổi Symfony

### 1. Entity: `Channel`
**File:** `src/Entity/Channel.php`

Thêm 2 settings mới per channel:

| Field | Type | Ý nghĩa |
|-------|------|----------|
| `wooSyncStockActive` | boolean | Bật/tắt sync stock lên WooCommerce cho channel này |
| `wooSyncStockInterval` | integer | Tần suất sync: 1, 2, hoặc 3 |

Constants:
```php
Channel::WOO_SYNC_INTERVAL_ONCE  = 1  // 1 lần/ngày (02:00)
Channel::WOO_SYNC_INTERVAL_TWICE = 2  // 2 lần/ngày (02:00, 15:00)
Channel::WOO_SYNC_INTERVAL_THREE = 3  // 3 lần/ngày (02:00, 11:00, 15:00)
```

### 1b. Entity: `Product`
**File:** `src/Entity/Product.php`

Thêm 1 field mới:

| Field | Type | Ý nghĩa |
|-------|------|----------|
| `wooSyncStock` | boolean (nullable, default false) | Bật/tắt export stock product này lên WooCommerce |

Chỉ product có `wooSyncStock = true` mới được include vào `stock.json`. Mặc định `NULL` (= false) — product cũ không bị export cho đến khi bật thủ công.

### 2. Repository: `ChannelRepository`
**File:** `src/Repository/ChannelRepository.php`

Thêm 2 filter vào method `query()`:
- `wooSyncStockActive` — lọc channel đang bật sync
- `wooSyncStockIntervalIn` — lọc theo mảng interval (dùng để chỉ chạy channel phù hợp với time slot)

### 3. Service: `ChannelService`
**File:** `src/Service/ChannelService.php`

- `generateItem()`: thêm `wooSyncStockActive` và `wooSyncStockInterval` vào response
- `generateUpdate()`: thêm `wooSyncStockActive` vào `$booleanFields` để convert đúng kiểu

### 3b. Service: `ProductService`
**File:** `src/Service/ProductService.php`

- `generateItem()`: thêm `wooSyncStock` vào response
- `update()`: thêm `wooSyncStock` vào `$booleanFields` để convert đúng kiểu

### 4. Service mới: `WooStockExportService`
**File:** `src/Service/WooStockExportService.php`

Luồng xử lý cho mỗi channel:
1. Query products của channel (join `products_channels`) có `wooSyncStock = true` — lấy `idWp`, `sku`, `stock`, `sizeId`, `size`
2. Build JSON theo đúng format WP đã dùng khi import vào Symfony:
   ```json
   {
     "channel_id": "Savehof_52",
     "items": [
       { "id": 2763, "type": "variable",  "parent_id": 0,    "sku": "JH030SIKJUL24", "stock": 4, "last_updated": "2026-05-12 14:00:00" },
       { "id": 2764, "type": "variation", "parent_id": 2763, "sku": "JH030SIKJUL24", "stock": 4, "size": "xs", "last_updated": "2026-05-12 14:00:00" },
       { "id": 2765, "type": "variation", "parent_id": 2763, "sku": "JH030SIKJUL24", "stock": 4, "size": "s",  "last_updated": "2026-05-12 14:00:00" }
     ]
   }
   ```
   - `id` = `products.idWp` (WooCommerce product/variation ID)
   - `type: variable` — product cha (`sizeId IS NULL`)
   - `type: variation` — product con có size (`sizeId NOT NULL`)
   - `sku` — base SKU (đã strip suffix `_{sizeId}` cho variation)
   - `parent_id` — `idWp` của product cha cùng base SKU; `0` nếu là variable
   - `size` — tên size (xs, s, m, l, xl, xxl, 3xl …); chỉ có ở variation
   - `last_updated` — `products.dateUpdated` format `Y-m-d H:i:s`
3. Lưu local tạm: `public/woo_stock_export/{channel.synId}/{Y-m-d_His}.json`
4. Upload lên **SFTP** (phpseclib3, port 22): `{woo_ftp_remote_path}/{channel.synId}/{Y-m-d_His}.json`
5. Upload thành công → xóa file local. Nếu SFTP chưa cấu hình (`woo_ftp_host` rỗng) thì file local được giữ lại để debug.

> Mỗi lần chạy tạo file mới (timestamp khác nhau) — không ghi đè file cũ. WP plugin tự pick file mới nhất trong folder `{channel.synId}/`.

> **Lưu ý:** SFTP chưa có thông tin — upload sẽ bị skip nếu `woo_sftp_host` rỗng. Chỉ cần điền params vào `services.yaml` khi có SFTP credentials từ Liem là tự chạy.

### 5. Command mới: `ExportStockToWooCommand`
**File:** `src/Command/Cronjob/ExportStockToWooCommand.php`

```bash
php bin/console cronjob:export-stock-to-woo --slot=night|noon|afternoon
```

Logic chọn channel theo slot:

| Slot | Giờ chạy | Chạy channels có interval |
|------|----------|--------------------------|
| `night` | 02:00 | 1, 2, 3 (tất cả) |
| `noon` | 11:00 | 3 |
| `afternoon` | 15:00 | 2, 3 |

Log ghi vào: `woo_stock_logs/export_{Y-m-d}.txt`

### 6. Migration
**File:** `migrations/Version20260512153705.php`

```sql
ALTER TABLE channels ADD woo_sync_stock_active BOOLEAN DEFAULT NULL;
ALTER TABLE channels ADD woo_sync_stock_interval INT DEFAULT NULL;
```

Đã chạy migrate thành công ngày 2026-05-12.

**File:** `migrations/Version20260513081208.php`

```sql
ALTER TABLE products ADD woo_sync_stock BOOLEAN DEFAULT NULL;
```

Đã chạy migrate thành công ngày 2026-05-13.

### 7. Config: `services.yaml`
**File:** `config/services.yaml`

```yaml
woo_ftp_host: ''          # SFTP host — điền khi có credentials từ Liem
woo_ftp_username: ''      # SFTP username
woo_ftp_password: ''      # SFTP password
woo_ftp_port: 22          # SFTP port (default 22)
woo_ftp_remote_path: '/woo_stock'
woo_stock_local_path: 'public/woo_stock_export'
```

> **Lưu ý:** Param tên `woo_ftp_*` nhưng thực tế dùng **SFTP** (không phải FTP thường). Khi xin info từ Liem cần yêu cầu **SFTP credentials**.

---

## Cron schedule cần setup trên server

> Giờ dưới đây tính theo **UTC** — nếu server dùng UTC+2 (Stockholm) thì cần trừ 2 giờ.

| Cron (UTC) | Slot | Chạy channel interval | Stockholm (UTC+2) |
|------------|------|-----------------------|-------------------|
| `0  2 * * *` | `night` | 1, 2, 3 (tất cả) | 04:00 |
| `0 11 * * *` | `noon` | 3 | 13:00 |
| `0 15 * * *` | `afternoon` | 2, 3 | 17:00 |

```cron
0  2 * * *  php bin/console cronjob:export-stock-to-woo --slot=night
0 11 * * *  php bin/console cronjob:export-stock-to-woo --slot=noon
0 15 * * *  php bin/console cronjob:export-stock-to-woo --slot=afternoon
```

---

## Thay đổi WooCommerce Plugin (Michael làm)

- Bỏ chức năng export stock từ WP → Symfony (gọi API `/import-wp-stock`)
- Thêm chức năng đọc file `stock.json` từ FTP folder và cập nhật stock trong WooCommerce
- Format JSON nhận vào giống hệt format WP đã dùng khi export sang Symfony (xem mục 4 ở trên)
- Match product bằng `id` (WooCommerce product/variation ID)

---

## Còn chờ

- [ ] SFTP credentials từ Liem (host, username, password, remote path) → điền vào `config/services.yaml`
- [ ] Michael update WooCommerce plugin: bỏ export stock, thêm import từ FTP
- [ ] Setup crontab trên server
- [ ] Test end-to-end sau khi có FTP

---

## Cách test thủ công

```bash
# Chạy thử slot night (sẽ pick up channels có wooSyncStockActive=true)
php bin/console cronjob:export-stock-to-woo --slot=night

# Xem log
cat woo_stock_logs/export_$(date +%Y-%m-%d).txt

# Xem file JSON được tạo (thay {synId} bằng channel.synId, ví dụ Savehof_52)
ls public/woo_stock_export/{synId}/
```

---

## Sample data tham khảo

**File:** `docs/sample_data/wp_import_stock_to_sf.json`

WP gửi vào Symfony (chiều cũ) — dùng làm chuẩn cho format JSON export (chiều mới).