**HEADLINE:** GO-WITH-ADJUSTMENTS — Lăng-kính FE/E2E/DEPLOY sạch về bản chất (wiring endpoint đúng, hợp đồng enum-int khớp byte-exact, mirror 2 app thật, Mig 63 an toàn prod); cần 1 adjust bắt buộc đã biết (bake header) + 1 catch MAJOR độc lập (nút Import dùng guard rộng hơn quyền endpoint).

LĂNG-KÍNH FE + E2E + DEPLOY (đọc source thật, working-tree chưa commit).

(1) FE gọi đúng endpoint + map response — PASS. Preview: SupplierImportDialog.tsx:118-122 gửi multipart field 'file' khớp param controller IFormFile file (SuppliersController.cs:67). Confirm: :134 gửi body { rows } khớp SupplierImportConfirmCommand(Rows). Type FE SupplierImportPreview/Result khớp 1-1 DTO (rows/newCount/updateCount/skipCount/errorCount/warnings/layoutValid).

(2) Hợp đồng enum-int render nhãn VN — PASS (verify sâu). BE SupplierType=1-6 / FE types/master.ts:11-18=1-6; SupplierTypeLabel:22-29 phủ đủ 1-6. SupplierStatus BE=FE=1-3. RowImportStatus BE 0-3 (SupplierImportDtos.cs:12-18)=FE const 0-3 (Dialog:19-24). CHỐT: KHÔNG có JsonStringEnumConverter (Program.cs:33-34 chỉ UtcDateTimeJsonConverter) → enum serialize thành INT, đúng kỳ vọng SupplierTypeLabel[r.type] (:305); feature Supplier list hiện hữu đã chứng minh int này ở prod.

(3) Confirm disable/hide — PASS. Hide khi layoutValid=false: nút chỉ render trong preview && preview.layoutValid (:188). Disable khi errorCount>0: disabled={preview.errorCount>0 || confirmMut.isPending} (:191).

(4) Round-trip rows verbatim — PASS. confirmMut.mutate(preview.rows) (:190) gửi nguyên rows. Field camelCase FE khớp PascalCase BE (authorizationNote/linkGuq/ownerPmh/supplierStatus/sourceUpdatedAt/existingSupplierId). Confirm TỰ re-derive New/Update từ DB + re-scan hard-error + NormalizeLengths (không tin status client) → an toàn. SourceUpdatedAt qua UtcDateTimeJsonConverter coi naive=UTC → KHÔNG lệch ngày cho VN (UTC+7).

(5) Invalidate list — PASS. qc.invalidateQueries(['suppliers']) (:140) prefix-match key list ['suppliers',{page,search,...}] (SuppliersPage.tsx:81) → list refresh.

(6) Mirror 2 app THẬT — PASS. SupplierImportDialog.tsx admin vs user byte-identical (13426 bytes, git diff exit 0); SuppliersPage.tsx byte-identical (exit 0); khối enum SupplierType/Status/Label trong types/master.ts identical giữa 2 app.

(7) E2E upload file thật (30 cột, 4 NCC) — PASS SAU BAKE. Luồng preview→bảng New/Update+counts→"Xác nhận nhập"→confirm→toast+invalidate+đóng: hợp lý đầu-cuối. 🔴 ADJUST BẮT BUỘC (gating): SupplierExcelImportService.cs:35-67 ExpectedHeaderTokens hiện BEST-GUESS ≠ 30 token thật; so khớp Ordinal-exact sau NormalizeHeader (:223) → chưa bake = MỌI file thật bị từ chối LayoutValid=false. Bake = thay 30 phần tử bằng đúng RealFileHeaderTokens có sẵn trong test (SupplierExcelImportServiceTests.cs:419-451). VERIFY: Test Case-9 (:310) robust 2 chiều LayoutValid.Should().Be(shouldMatch) → sau bake shouldMatch=true → tự khẳng định LayoutValid=true, 10/10 test giữ xanh, KHÔNG cần sửa test. Claim "tự lật khi bake" ĐÚNG.

(8) DEPLOY-RISK prod live — GO. Dup-risk 4 NCC: xác nhận TRUONGGIANG/TANPHU/TGN/DONGDUONG seed UNGATED mỗi startup tại DbInitializer.cs:2775-2781 (SeedRealMasterDataAsync, comment :2769 "Import Excel Database NCC anh Kiệt"), Code=col-4 file. Re-import → khớp CI theo Code → Update → FillNulls (:381-409 chỉ điền field NULL, không đè non-null) → KHÔNG nhân đôi, KHÔNG đè. AN TOÀN. Sắc thái: là fill-nulls (làm giàu cột mở rộng đang null), KHÔNG phải no-op tuyệt đối; fill-nulls-safe = re-import KHÔNG sửa được field đã có giá trị (vd Status). Mig 63 20260712070117_AddSupplierImportSourceFields: 2 AddColumn nullable (SourceUpdatedAt datetime2 null + SourceUpdatedBy nvarchar(200) null) → metadata-only SQL Server, an toàn apply live; 3-file rule OK (.cs+.Designer.cs+ModelSnapshot đều có 2 cột); SupplierConfiguration.cs:45 HasMaxLength(200) khớp.

CATCH ĐỘC LẬP (không có trong brief): 🟠 MAJOR (FE guard/authz-scope mismatch). Nút "Import Excel NCC" bọc PermissionGuard menuKey=Suppliers action="Create" (SuppliersPage.tsx:214-220, mirror 2 app), NHƯNG 2 endpoint import yêu cầu [Authorize(Roles="Admin,CatalogManager")] (SuppliersController.cs:64,77). Theo S59, endpoint Create (POST /suppliers) MỞ cho mọi user đăng nhập → user có quyền-menu Suppliers.Create nhưng KHÔNG có role Admin/CatalogManager (điển hình user thường fe-user) sẽ THẤY nút Import nhưng bấm → 403 ở preview. BE AN TOÀN (không lỗ hổng), là khiếm khuyết UX: guard rộng hơn authz endpoint. Khuyến nghị gate nút Import theo role Admin/CatalogManager (hoặc ẩn ở fe-user) cho khớp. Không phải blocker deploy.

POSITIVE (kháng soi mói): 24 hằng Max* trong service (:76-81) khớp BYTE-EXACT độ dài cột EF → không có bug 500-on-save; dedup CI in-memory (không dựa DB) chặn unique-violation 500 prod (test chứng qua SQLite binary collation); all-or-nothing + fill-nulls-safe; multipart header set tay khớp 4 upload đang chạy prod (ContractAttachments/PeDetailTabs/FormsPage) → không phải bug boundary; DI/config/snapshot đăng ký đủ.

PHẠM VI: KHÔNG tự chạy lại dotnet build/test/npm build — xác minh bằng nhất quán tĩnh (DI+config+snapshot+DTO+controller+command khớp; 10 [Fact] đếm đúng; collection-expression C#12 hợp .NET 10). Nhận claim build EXIT-0 + 450 PASS ở mức tin-cậy-có-điều-kiện.

## Open Questions
1. [OWNER — em main/anh Kiệt] Nút "Import Excel NCC" có nên hiện ở fe-user cho user thường không? Guard hiện = Suppliers.Create (rộng) trong khi endpoint = Admin/CatalogManager (hẹp) → user thường thấy nút nhưng 403 khi upload. Đề xuất đổi guard nút Import sang điều kiện role Admin/CatalogManager cho khớp endpoint, hoặc bỏ dialog khỏi fe-user (import hàng loạt là thao tác admin). Không phải blocker (BE chặn đúng) nhưng nên xử trước/ngay sau deploy.

2. [VERIFY khi bake — rủi ro E2E không thể loại trừ tại đây] 30 token bake lấy từ brief/test, CHƯA đối chiếu file .xlsx thật trên máy anh Kiệt. Fingerprint exact-match + hardcode HeaderRow=4/DataStartRow=5 + map theo chỉ-số-cột-tuyệt-đối. Nếu file thật lệch (space/ký-tự-ẩn/hàng-header-khác/cột-thừa) → bị từ chối (fail-safe, có "Header nhận được" trong hộp đỏ FE để copy về sửa 1 vòng, KHÔNG hỏng dữ liệu). Khuyến nghị: upload file thật 1 lần (prod hoặc bản copy) xác nhận LayoutValid=true TRƯỚC khi tuyên bố xong.

3. [Xác nhận nhỏ] fill-nulls-safe khiến re-import KHÔNG sửa được field đã có giá trị ở 4 NCC seed (vd Status seed=DangHoatDong sẽ không đổi dù Excel ghi Blacklist). Đúng ý decision-4 chứ? Nếu cần "Excel thắng" cho vài cột (Status/Note) thì phải chốt riêng.
