# invest-synthesis — S112 Supplier Excel-import (Phase B)

> 🔄 **partial-honest — CHUẨN-HOÁ VỊ-TRÍ @S122 (2026-07-15), KHÔNG PHẢI VIẾT MỚI.**
>
> Thân synthesis dưới = **trích từ `run.md:25-52`**, đã tồn-tại từ **2026-07-12**.
> 🔴 **Bản gốc trong `run.md` GIỮ NGUYÊN, và bản gốc mới là CHUẨN** — file này là bản-sao chuẩn-hoá vị-trí. Không cắt, không rewrite history (tinh-thần C8 `workflows/README.md:19`).
>
> **Vì sao file này ra đời:** orphan-authority (`workflows/README.md:20`) đo **TÊN FILE**, không đo **nội-dung**. Đo @S122: `naive 13 / dual-accept 8 / C8 cứu 5` — **0/7 là việc mất**. Anh chủ-dự-án chốt @S122: **chuẩn-hoá + tri-accept**.

---

## [TRÍCH `run.md:25-52` — bản gốc là chuẩn]

### synthesis (em-main single-writer — wf_e2f23a84-933, 4/4 clean, ~416K tok Opus)

> ⚠️ Sub-file naming: journal completion-order ≠ LANES order → `sub-invest-codebase-B-approach.md` HOLDS Lane C (dedup/edge) content · `sub-invest-codebase-C-edge.md` HOLDS Lane B (approach) content. Content correct, filenames swapped (harvest reads content).

**REVIEWER VERDICT: PASS_WITH_CONCERNS.** Design sound; every in-repo file:line verified KHỚP (0 fabrication); 27/27 field bijective, 0 silent loss. 1 MAJOR + 4 MINOR refine it before build.

#### Load-bearing findings
1. **Schema 100% ready** — 30 Excel col → 27 field bijective (1 ignore STT, 27 direct col2-28, 2 audit-provenance col29-30 = owner-call). Mig 62's 18-field expansion purpose-built for this Excel. 0 invented field.
2. **🎁 Approach B ĐÃ SHIP** — spec "seed fills partial" is WRONG: `DbInitializer.cs:2775-2782` fills all 30 fields for the 4 NCC, prod-verified (Mig 62 `778fc98` Run #488). **This exact file's 4 NCC are already in prod.**
3. **ClosedXML 0.105 already in `SolutionErp.Infrastructure.csproj:8`** (0 new dep); read-idiom `XlsxRenderer.cs:18-25`; IFormFile plumbing in 3 controllers. NamGroup `HrEmployeeImportService` = complete upload→preview→confirm pattern to port (⚠️ MINOR: unverifiable from this env — repo ERP_MINI_FINAL absent).
4. **🔴 MAJOR (reviewer caught, all 3 lanes missed):** dedup Code = `Ordinal` (case-sensitive) vs DB unique-index default = case-INSENSITIVE collation → Approach A with case-variant Code → dict-miss → INSERT → hard unique-violation 500. **Precondition:** normalize Code (Trim+UPPER or OrdinalIgnoreCase) before dedup+store+preview.
5. **Must-knows:** import writes entity directly with fill-where-non-empty, NOT via `UpdateSupplierCommand` (blind absolute-set → NULL-clobber #73); read by absolute cell index NOT `CellsUsed()` (skips empties → drift); parser anchors r4 + fingerprint-guard; 2-pass all-or-nothing (~4-20 rows).

#### 3 approach
| | Effort | Reusable | Verdict |
|---|---|---|---|
| **A upload endpoint** | MED (port NamGroup) | ✅ | best if NCC keep coming; needs MAJOR fix first |
| **B extend seed** | ~0 (ĐÃ SHIP file này) | ❌ | done for these 4 NCC; no update-sync |
| **C import-command** | MED | ~ | bỏ — cắt preview = cắt lá chắn cho Excel bẩn |

#### 5 owner decisions
1. Col 29-30 (NGÀY/NGƯỜI CẬP NHẬT) → ignore / Note / +2 field Mig 63?
2. Code = col4 "TÊN VIẾT TẮT" confirmed (= mã HĐ key RG-001)?
3. Reusable tool needed? → A ; chỉ file này → B đã xong.
4. Re-import existing Code → fill-nulls (safe) vs overwrite-non-empty (updates, destructive)?
5. PHÂN LOẠI unknown value → reject-row vs default NhaCungCap?

**Harvest:** pending @closeout — em-main append 1 entry investigator-codebase + 1 reviewer, stamp `[engine: fable-clone-ensemble · Opus-S112]`.

---

## Verify độc-lập @S122 (lead — KHÔNG tin thẳng return sub)

**VERDICT: LANDED** — `e100ef0` · 2026-07-12T16:13:46+07:00 · *"[CLAUDE] Supplier: Excel-import Phase B (upload NCC preview/confirm) + Mig 63"* — **20 file, +8268/−14**. Approach **A** shipped (không phải B).

- **Mig 63 trên đĩa:** `Persistence/Migrations/20260712070117_AddSupplierImportSourceFields.cs` (+ `.Designer.cs` + ModelSnapshot = **3-file rule ĐỦ**), 2 AddColumn / 2 DropColumn ở Down (reversible).

**🔴 Cả 5 owner-decision → RESOLVED, và truy được TRONG CODE** (không phải chỉ trong doc):

| Decision (`run.md:46-50`) | Chốt | Evidence trên đĩa |
|---|---|---|
| D1 col 29-30 | option (c) **+2 field Mig 63** | `SupplierExcelImportService.cs:320` `SourceUpdatedAt = ReadDate(ws, r, 29)` |
| D2 Code = col4 | **CONFIRMED** | `:43` `"TÊN VIẾT TẮT\n(Dùng trong HĐ)", // 4 → Code (upsert key)` |
| D3 reusable? | → **A** (endpoint) | `SuppliersController.cs:79/:87/:100` template/preview/confirm |
| D4 re-import | **fill-nulls** (an-toàn) | 27× pattern `is null && r.<X> is not null` (vd `:456`) |
| D5 Type lạ | **default NhaCungCap** | `:376` `return SupplierType.NhaCungCap; // unknown → default (decision 5, không reject)` ← **code cite THẲNG số decision** |

**Reviewer MAJOR + 3 MINOR → FIXED, verbatim nằm trong header code đã ship:**
- `SupplierExcelImportService.cs:18-21` — *"🔴 CRITICAL: dedup Code phải CASE-INSENSITIVE (unique-index SQL Server = CI)… Dùng `StringComparer.OrdinalIgnoreCase` ở CẢ preview + confirm"* (dict `:175, :176, :495, :509`)
- `:15` — *"Đọc theo CHỈ SỐ CỘT TUYỆT ĐỐI (row.Cell(i)) — KHÔNG CellsUsed()"*

**Test:** `tests/…/Services/SupplierExcelImportServiceTests.cs` = **10 [Fact/Theory]** (khớp commit-body "10 test").
**Kế-thừa:** S113 `5fa11b5` build tiếp trên nền này (import v2 + Mig 64).

> 🟢 **Đáng ghi:** MAJOR mà reviewer bắt (case-sensitivity dedup vs CI-collation) **cả 3 invest-lane đều miss** — và nó được vá **trước khi build**, lời cảnh-báo còn được đóng-đinh vào header file production. Đây là ca reviewer-lane trả giá đúng bằng giá-trị của nó.

### Gap KHÔNG chứng minh được (khai thẳng)

1. **NamGroup `HrEmployeeImportService`** — reviewer đã tự khai MINOR *"unverifiable from this env — repo ERP_MINI_FINAL absent"*. S122 **cũng không verify được** ⇒ giữ nguyên nấc.
2. **Prod Run #496 / Mig 63 applied prod** — chỉ có claim trong commit-message; S122 KHÔNG probe CI/prod ⇒ **KHÔNG XÁC ĐỊNH ĐƯỢC**.
3. **10 test PASS** — S122 KHÔNG chạy `dotnet test`; chỉ đếm được 10 `[Fact/Theory]` **tồn tại**.

### Lệch convention của chính folder này (ghi để không lặp)

- Sub-MD naming **swapped** (`B-approach` giữ nội-dung Lane C, `C-edge` giữ Lane B) — synthesis tự khai. **Tái-diễn ×3** (folder này + `pe-signoff-save-advance` + `supplier-import-close-review`) ⇒ đây là **lỗi hệ-thống của journal-order, không phải sơ-suất lẻ**.
- Synthesis ghi vào **thân `run.md`** ⇒ **dạng-3**, nguyên-nhân gốc false-orphan.

---
*Chuẩn-hoá vị-trí @S122 `/tiep` · run `2026-07-15-S122-retro-harvest-7-orphan` · wf `wf_5ce007c4-980` lane `retro:S112-supplier-excel-import` · lead Opus 4.8 · sub read-only, lead single-writer (B6)*
