# sub-invest-codebase-A-mapping **HEADLINE:** 30 cột Excel map GỌN vào 27 field Supplier: 1 ignore (STT) + 27 direct-field (cột 2–28, mỗi field dùng đúng 1 lần) + 2 cột audit-provenance (NGÀY/NGƯỜI CẬP NHẬT) KHÔNG có field khớp trực-tiếp → cần owner quyết. Seed 4 NCC (DbInitializer.cs:2775-2782) đã là precedent transform sống cho 27 mapping. ## LANE A — COLUMN→FIELD MAPPING (verified vs source thật) **Nguồn verify:** `src/Backend/SolutionErp.Domain/Master/Supplier.cs:5-36` (27 field), `SupplierType.cs:3-11`, `SupplierStatus.cs:3-8`, precedent transform `DbInitializer.cs:2775-2782` (seed 4 NCC thật đã map 30→27), audit `AuditingInterceptor.cs:44-49` + `BaseEntity.cs:6-9`. ### Bảng mapping đủ 30 dòng | # | Excel col (r4) | → target field | kind | transform / note | |---|---|---|---|---| | 1 | STT | — | **ignore** | Số dòng, không persist | | 2 | GÓI THẦU | `PackageCategory` | direct-field | Free-text. Seed vd `"10-Nhôm kính+Face Mặt dựng"` | | 3 | PHÂN LOẠI (NTP/NCC/Cả hai) | `Type` (`SupplierType`, **non-null**) | **enum-map** | Trim+ci: `NCC`→NhaCungCap(1) · `NTP`→NhaThauPhu(2) · `Cả hai`→CaHai(6). (Enum còn TĐ=3/ĐVDV=4/CĐT=5 nếu source có). Non-nullable → giá trị lạ phải reject-row hoặc default (owner) | | 4 | TÊN VIẾT TẮT | `Code` (**required**) | direct-field | = Mã NCC dùng trong mã HĐ. Trim+UPPER khuyến nghị (seed: `TRUONGGIANG`). Là **khóa idempotent/dedup** | | 5 | TÊN CÔNG TY | `Name` (**required**) | direct-field | Tên đầy đủ. KHÔNG tạo cột `companyName` mới — reuse `Name` gốc | | 6 | ĐỊA CHỈ XUẤT HÓA ĐƠN | `Address` | direct-field | = "địa chỉ xuất HĐ/đăng ký" (comment field khớp). Reuse cột gốc, KHÔNG tạo `invoiceAddress` | | 7 | ĐỊA CHỈ VĂN PHÒNG | `OfficeAddress` | direct-field | 4 NCC seed để trống → field vẫn là target đúng | | 8 | SỐ ĐT CÔNG TY | `Phone` | direct-field | Phone công-ty (phân biệt cột 21) | | 9 | FAX | `Fax` | direct-field | Chỉ TGN có (seed `"0251.2814663"`) | | 10 | SỐ TÀI KHOẢN+TÊN+CN.NH | `BankAccount` | direct-field · **COMPOSITE** | **Giữ RAW 1 ô** (precedent: `"100212127 Ngân hàng Eximbank - Chi Nhánh Hòa Bình"`). Entity KHÔNG có AccountName/BankBranch → không tách | | 11 | SỐ TK PHỤ | `SecondaryBankAccount` | direct-field | 4 NCC seed để trống, field đúng target | | 12 | MÃ SỐ THUẾ | `TaxCode` | direct-field | Giữ raw kể cả có space (`"0312 251 859"`). Có thể null (DONGDUONG) | | 13 | NGƯỜI ĐẠI DIỆN PHÁP LUẬT | `LegalRepresentative` | direct-field | Giữ prefix `"Ông/Bà"` như seed | | 14 | CHỨC VỤ ĐẠI DIỆN | `LegalRepTitle` | direct-field | vd `"GIÁM ĐỐC ĐIỀU HÀNH"` | | 15 | GIẤY ỦY QUYỀN | `AuthorizationNote` | direct-field | Free-text dài (số GUQ+ngày+người ủy quyền) | | 16 | Link GUQ | `LinkGuq` | direct-field · **NAS path** | **Giữ RAW backslash** `@"01. GPKD_HSNL_GUQ\02.Trường giang\...pdf"` — KHÔNG normalize `\`→`/` | | 17 | Link GPKD | `LinkGpkd` | direct-field · NAS path | như trên | | 18 | Link HSNL | `LinkHsnl` | direct-field · NAS path | như trên | | 19 | NGƯỜI LH CHÍNH | `ContactPerson` | direct-field | Người liên hệ chính (seed `"Mr. Việt"`) | | 20 | CHỨC VỤ NGƯỜI LH | `ContactTitle` | direct-field | vd `"Kinh doanh"` | | 21 | SĐT CHÍNH | `ContactPhone` | direct-field | Phone người-LH (KHÁC cột 8 phone công-ty) | | 22 | EMAIL | `Email` | direct-field | | | 23 | ĐỊA CHỈ GỬI THƯ | `MailingAddress` | direct-field | Địa chỉ thứ-3 (khác cột 6 & 7) | | 24 | NGƯỜI NHẬN THƯ/SĐT | `MailRecipient` | direct-field · **COMPOSITE** | **Giữ RAW** `"MS Thủy / 036 405 0016"` (name+phone gộp). Entity không có field tách | | 25 | NGUỒN GIỚI THIỆU | `ReferralSource` | direct-field | vd `"Tái sử dụng DA trước"` | | 26 | NGƯỜI PHỤ TRÁCH(PMH) | `OwnerPmh` | direct-field | vd `"Bình"` | | 27 | TÌNH TRẠNG HIỆN TẠI | `Status` (`SupplierStatus?`, **nullable**) | **enum-map** | **Strip emoji + trim**: `✅ Đang hoạt động`→DangHoatDong(1) · `Blacklist`/`đen`→Blacklist(2) · `Ngừng hợp tác`→NgungHopTac(3). Nullable → giá trị lạ = null OK | | 28 | GHI CHÚ/LÝ DO BLACKLIST | `Note` | direct-field | Target ĐÚNG cho Note. (Seed để `Note=null` vì đã un-cram legacy — import thì col 28 mới là Note thật; coherence: khi Status=Blacklist thì lý-do nằm ở đây) | | 29 | NGÀY CẬP NHẬT CUỐI | **(audit)** `UpdatedAt`? | **KHÔNG có field khớp** | ⚠️ Map `UpdatedAt` = **futile**: `AuditingInterceptor.cs:48` GHI ĐÈ `UpdatedAt=now` mỗi save. → ignore / gộp-Note / đề-xuất-field-mới (xem openQuestions) | | 30 | NGƯỜI CẬP NHẬT | **(audit)** `UpdatedBy`? | **KHÔNG có field khớp** | ⚠️ `UpdatedBy` là `Guid?` (userId), Excel là **tên người** (text) → type mismatch + bị `AuditingInterceptor.cs:49` ghi đè. Không direct-map được | ### Kiểm chứng độ-phủ (0 sót) - **27/27 field entity** được map đúng 1 lần (9 core + 18 expansion) — cột 2–28 (27 cột). - **Cột 1** STT = ignore. - **Cột 29–30** = audit-provenance, KHÔNG field khớp trực tiếp → quyết định owner. - Tổng: 1 ignore + 27 direct + 2 no-field = **30 ✓**. ### 3 nhóm cần transform đặc-biệt (đã đánh dấu trong bảng) - **COMPOSITE (2):** cột 10 BankAccount + cột 24 MailRecipient → **giữ RAW** (precedent seed đã giữ raw, entity thiếu sub-field để tách). - **ENUM-MAP (2):** cột 3 Type (trim+ci, non-null → cần rule giá-trị-lạ) + cột 27 Status (strip emoji, nullable → lạ=null). - **NAS path (3):** cột 16/17/18 → giữ raw backslash, KHÔNG đổi `\`→`/`. - **Chung mọi cột:** normalize `\n`/whitespace trong header r4 (multi-line) + trim cell; skip `#REF!` ở r2 (chỉ parse tên r4). ### Lưu ý reuse-cột (tránh orphan) Cột 5/6 map thẳng `Name`/`Address` gốc — KHÔNG tạo `companyName`/`invoiceAddress` mới (sẽ orphan cột seed live). 18 expansion field vốn purpose-built đúng cho Excel này (Mig 62), khớp 1-1 cột 2,7,9,10,11,13-18,20,21,23-27. ## Open Questions **1. Cột 29-30 (NGÀY/NGƯỜI CẬP NHẬT) xử lý sao?** — KHÔNG có field khớp (audit fields `UpdatedAt`/`UpdatedBy` bị `AuditingInterceptor` ghi đè + `UpdatedBy` là Guid≠tên). 3 lựa chọn: (a) **ignore** — audit tự stamp import-time (mất provenance nguồn); (b) **gộp-Note** — nối `"Cập nhật cuối 01/07/2027 bởi Bình"` vào `Note` (giữ provenance, không thêm schema); (c) **đề-xuất-field-mới** `SourceUpdatedAt DateTime?` + `SourceUpdatedBy string?` (+2 cột Mig 63, sạch nhất nhưng thêm migration). **2. PHÂN LOẠI (col 3, non-nullable Type) khi gặp giá-trị-lạ** ngoài NTP/NCC/Cả hai → reject-row hay default về NhaCungCap? (Status nullable nên không vướng — lạ=null). **3. Composite cột 10 & 24 — xác nhận giữ RAW** (theo precedent seed) hay owner muốn tách sub-field (cần thêm cột AccountName/BankBranch/RecipientPhone → Mig mới)? **4. Code (col 4) là khóa idempotent** — import gặp Code đã tồn tại thì skip / fill-nulls / overwrite? (Ảnh hưởng Lane B robustness).