# SPEC — Supplier Excel-Import (Phase B unpark) — 12-07-2026

> Engine-đắt `/fable-clone vai invest/review` (H21 LỆNH-B) — anh-directed S112. Lead ghi SAU khi verify (propose→verify→ghi→thực-thi). Worker nhận spec QUA prompt (không tự Read file này). Model = Opus 4.8, effort inherit xhigh. RUN-TRACE folder = `runs/2026-07-12-S112-supplier-excel-import/`.

## ① Tính-năng / đề-bài

Thiết kế tính năng **import Excel "Database NCC" vào module Supplier (NCC / Nhà thầu phụ / Tổ đội)** — unpark Phase B (parked S96/S104). Input = `C:\Users\pqhuy\Downloads\Data base - 1_7_2027 (3).xlsx` (anh Kiệt cung cấp).

🔴 **RUN NÀY = ĐIỀU TRA + THIẾT KẾ, KHÔNG CODE.** Deliverable = (a) bảng mapping cột Excel → field Supplier ĐỦ 30 cột, (b) 3 approach + tradeoff + MVP recommendation, (c) review đối kháng. Anh quyết approach + scope sau.

### Excel thực tế (đã scout bằng openpyxl — ground-truth):
- 1 sheet `Sheet1`, `max_row=8 max_col=31`.
- Header = **4 dòng gộp**: r1 = title "DATABASE NHÀ CUNG CẤP & NHÀ THẦU PHỤ" · r2 = số cột 1-18 rồi **#REF!** (col 21-31) · r3 = nhóm (ID | THÔNG TIN ĐƯA VÀO HỢP ĐỒNG | THÔNG TIN LIÊN HỆ | TÌNH TRẠNG) · **r4 = TÊN CỘT THẬT.**
- Data từ **row 5**, chỉ **4 NCC** (TRUONGGIANG · TANPHU · TGN · +1) — sample nhỏ.
- 30 cột thật (r4): `STT · GÓI THẦU · PHÂN LOẠI(NTP/NCC/Cả hai) · TÊN VIẾT TẮT · TÊN CÔNG TY · ĐỊA CHỈ XUẤT HÓA ĐƠN · ĐỊA CHỈ VĂN PHÒNG · SỐ ĐT CÔNG TY · FAX · SỐ TÀI KHOẢN+TÊN+CN.NH · SỐ TK PHỤ · MÃ SỐ THUẾ · NGƯỜI ĐẠI DIỆN PHÁP LUẬT · CHỨC VỤ ĐẠI DIỆN · GIẤY ỦY QUYỀN · Link GUQ · Link GPKD · Link HSNL · NGƯỜI LH CHÍNH · CHỨC VỤ NGƯỜI LH · SĐT CHÍNH · EMAIL · ĐỊA CHỈ GỬI THƯ · NGƯỜI NHẬN THƯ/SĐT · NGUỒN GIỚI THIỆU · NGƯỜI PHỤ TRÁCH(PMH) · TÌNH TRẠNG HIỆN TẠI · GHI CHÚ/LÝ DO BLACKLIST · NGÀY CẬP NHẬT CUỐI · NGƯỜI CẬP NHẬT`.
- Messy đặc thù: header multi-line (`\n` trong tên cột) · NAS path backslash (`01. GPKD_HSNL_GUQ\02.Trường giang\...` ở Link GUQ/GPKD/HSNL) · composite cell (SỐ TÀI KHOẢN = "số TK + tên + CN ngân hàng" gộp 1 ô) · #REF! cols · giá trị TÌNH TRẠNG có emoji ("✅ Đang hoạt động").

### Supplier entity hiện tại (Mig 62 — 27 field, all expansion nullable) — `src/Backend/SolutionErp.Domain/Master/Supplier.cs`:
- 9 gốc: `Code · Name · Type(SupplierType) · TaxCode · Phone · Email · Address · ContactPerson · Note`.
- 18 expansion (Mig 62, purpose-built cho Excel này): `PackageCategory · OfficeAddress · MailingAddress · Fax · BankAccount · SecondaryBankAccount · LegalRepresentative · LegalRepTitle · AuthorizationNote · LinkGuq · LinkGpkd · LinkHsnl · ContactTitle · ContactPhone · MailRecipient · ReferralSource · OwnerPmh · Status(SupplierStatus)`.
- `SupplierType`: NhaCungCap=1(NCC) · NhaThauPhu=2(NTP) · ToDoi=3 · DonViDichVu=4 · ChuDauTu=5 · **CaHai=6(Cả hai)**.
- `SupplierStatus`: DangHoatDong=1 · Blacklist=2 · NgungHopTac=3.
- 🔴 **KHÔNG có import endpoint** (grep SuppliersController = 0 hit `import|upload|IFormFile|.xlsx`). Đường DUY NHẤT hiện tại = `SeedRealMasterDataAsync` (`DbInitializer.cs:2590`, hardcode C# 4 NCC, fill PARTIAL — KHÔNG đụng 18 field expansion).

## ② Cách implement (điều tra + đề xuất — KHÔNG code lượt này)

3 lăng-kính `investigator-codebase` (ensemble same-role, parallel) → `reviewer` verify → em-main synthesize:
- **Lane A — MAPPING:** map chính xác 30 cột r4 → 27 field Supplier. Mỗi cột phân loại: `→field trực tiếp` | `→Note (gộp)` | `→field mới (đề xuất)` | `ignore`. Đánh dấu composite (BankAccount/MailRecipient — giữ raw hay tách?) + enum-map (NTP/NCC/Cả hai→SupplierType; ✅Đang hoạt động→SupplierStatus). 0 cột bỏ sót.
- **Lane B — CODE + APPROACH:** shape `CreateSupplierCommand`/`UpdateSupplierCommand`; cách `SeedRealMasterDataAsync` fill (partial); **lib đọc xlsx codebase ĐÃ có chưa** (form-engine dùng ClosedXML/OpenXml? grep + file:line) — nếu có thì tái dùng, không thì đề xuất; pattern reference (NamGroup có Excel-import không?). Đề xuất **3 approach + tradeoff:** (A) upload-endpoint FE→BE parse→preview→confirm-upsert · (B) extend SeedRealMasterDataAsync fill đủ 27 field 4 NCC (one-time, không reusable) · (C) admin import-command (button, server-side/upload). MVP recommendation cho solo-dev + 4-row sample.
- **Lane C — DATA-INTEGRITY / EDGE:** parser cho header-4-dòng (đọc từ r4, data r5) + #REF! cols (bỏ) + NAS backslash path (giữ nguyên → LinkGuq/Gpkd/Hsnl); **dedup key** (Code=TÊN VIẾT TẮT vs TaxCode=MST; xử TaxCode-null như DONGDUONG); upsert idempotent (mirror seed philosophy per-code); validation (MST format, required field); ≥5 edge-case cụ thể + cách xử.

## ③ Checklist (deliverable + acceptance ĐO ĐƯỢC)

- [ ] **Lane A** — bảng mapping ĐỦ 30 cột (cột | target-field | kind | transform); 0 sót; composite + enum đánh dấu rõ.
- [ ] **Lane B** — bảng 3 approach × tradeoff; chỉ ra lib xlsx sẵn có (file:line) HOẶC "chưa có"; 1 MVP-recommendation có lý do.
- [ ] **Lane C** — dedup key + lý do (xử null); ≥5 edge-case + cách xử; parser-strategy cho messy header (r4-start, #REF!, backslash).
- [ ] **Reviewer** — verify: (1) mapping 0-silent-loss ĐỦ 30 cột; (2) dedup-key đúng + null-safe; (3) approach sound cho solo-dev; (4) fidelity 27-field (0 field bịa / 0 field sót). Verdict PASS/PASS_WITH_CONCERNS/FAIL + gap file:line.
- [ ] **em-main** — 1 design-doc synthesis + option cho anh quyết + agent-memory entry (investigator-codebase + reviewer, stamp `[engine: fable-clone-ensemble · Opus-S112]`).
