diff --git a/scripts/s89-delete-test-users.sql b/scripts/s89-delete-test-users.sql new file mode 100644 index 0000000..a4bf88d --- /dev/null +++ b/scripts/s89-delete-test-users.sql @@ -0,0 +1,76 @@ +-- ============================================================================ +-- S89 (2026-06-25) — XÓA hết User TEST, chỉ giữ 17 User Solutions THẬT (+ admin) +-- Chỉ đạo: anh Kiệt FDC ("Xóa hết các User test ko liên quan, lấy User Solution thật vào"). +-- +-- Whitelist-keep (AN TOÀN): xóa MỌI User KHÔNG nằm trong 17 keep bên dưới. +-- Recon S89 (prod): 55 user -> KEEP 17 (admin + 16 real) -> DELETE 38: +-- - 3 active tên-giả: nv.cao, nv.test, nv.truong +-- - 34 đã-khóa: act.* bod.* ccm.tran equ.* fin.* hra.* nv.dinh pm.* pro.pham qs.* +-- - 1 typo domain: chuong.phan@solution.com.vn (anh chốt xóa — anh Chương dùng bản @solutions) +-- FK an toàn: KHÔNG user test nào là ApproverUserId (NO_ACTION không bị chặn — recon verified). +-- 5 bảng con CASCADE tự xóa: EmployeeProfiles, UserRoles, UserClaims, UserLogins, UserTokens. +-- nv.test còn 1 PE (DrafterUserId loose-Guid, KHÔNG FK) -> xóa kèm (cascade child, proven S85). +-- Safety: SET XACT_ABORT ON (lỗi -> rollback toàn bộ) + BACKUP bước 0 (-b abort nếu backup fail) +-- + count-guard [30,45] (kỳ vọng 38) -> chặn nếu delete-set bất thường. +-- Run: scp scripts/s89-delete-test-users.sql vietreport-vps:C:/temp/s89.sql +-- ssh vietreport-vps "sqlcmd -S .\SQLEXPRESS -d SolutionErp -i C:\temp\s89.sql -f 65001 -b" +-- ============================================================================ +SET QUOTED_IDENTIFIER ON; +SET ANSI_NULLS ON; +SET NOCOUNT ON; +SET XACT_ABORT ON; +GO + +-- ===== 0. BACKUP insurance (NGOÀI transaction; -b -> backup fail thì abort TRƯỚC delete) ===== +DECLARE @bkdir nvarchar(512), @bkfile nvarchar(600); +EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'BackupDirectory', @bkdir OUTPUT; +SET @bkfile = @bkdir + N'\SolutionErp_S89_preuserdelete.bak'; +PRINT 'Backup device -> ' + @bkfile; +BACKUP DATABASE [SolutionErp] TO DISK = @bkfile WITH INIT, CHECKSUM, NAME = N'SolutionErp-S89-pre-user-delete'; +GO + +-- ===== 1. Keep-list 17 (admin + 16 user Solutions thật) ===== +DECLARE @keep TABLE (Email nvarchar(256) PRIMARY KEY); +INSERT INTO @keep (Email) VALUES + (N'admin@solutions.com.vn'), (N'anh.nguyen@solutions.com.vn'), (N'binh.le@solutions.com.vn'), + (N'binh.lethanh@solutions.com.vn'), (N'catalog.manager@solutions.com.vn'),(N'chuong.phan@solutions.com.vn'), + (N'duy.nguyen@solutions.com.vn'), (N'long.chau@solutions.com.vn'), (N'long.nguyen@solutions.com.vn'), + (N'luu.tran@solutions.com.vn'), (N'nguyen.ho@solutions.com.vn'), (N'phuong.nguyen@solutions.com.vn'), + (N'thanh.lethanh@solutions.com.vn'), (N'tra.bui@solutions.com.vn'), (N'tring.le@solutions.com.vn'), + (N'truong.le@solutions.com.vn'), (N'truong.nguyen@solutions.com.vn'); + +DECLARE @del TABLE (Id uniqueidentifier PRIMARY KEY, Email nvarchar(256)); +INSERT INTO @del (Id, Email) + SELECT Id, Email FROM Users WHERE Email NOT IN (SELECT Email FROM @keep); + +PRINT '=== USERS TO DELETE ==='; +SELECT Email FROM @del ORDER BY Email; +DECLARE @cnt int = (SELECT COUNT(*) FROM @del); +PRINT 'Count to delete = ' + CAST(@cnt AS varchar(10)) + ' (expect 38)'; + +-- Count-guard: chặn nếu delete-set rơi ngoài khoảng an toàn (vd keep-list sai -> xóa nhầm user thật) +IF @cnt < 30 OR @cnt > 45 +BEGIN + RAISERROR('S89 ABORT — delete count %d ngoai khoang an toan [30,45]. KHONG xoa.', 16, 1, @cnt); + RETURN; +END + +-- ===== 2. Thực thi xóa (transaction, XACT_ABORT bao ve) ===== +BEGIN TRANSACTION; + + -- 2a. PE test của user bị xóa (chỉ nv.test có 1 PE — DrafterUserId loose-Guid, cascade child khi DELETE PE) + DELETE FROM PurchaseEvaluations WHERE DrafterUserId IN (SELECT Id FROM @del); + PRINT ' Test PE deleted : ' + CAST(@@ROWCOUNT AS varchar(10)); + + -- 2b. Xóa user (CASCADE: EmployeeProfiles / UserRoles / UserClaims / UserLogins / UserTokens) + DELETE FROM Users WHERE Id IN (SELECT Id FROM @del); + PRINT ' Users deleted : ' + CAST(@@ROWCOUNT AS varchar(10)); + +COMMIT TRANSACTION; +PRINT 'S89 delete COMMITTED.'; + +-- ===== 3. Verify AFTER (kỳ vọng còn đúng 17 user thật) ===== +PRINT '=== AFTER — remaining users (expect 17) ==='; +SELECT Email FROM Users ORDER BY Email; +SELECT COUNT(*) AS RemainingUsers FROM Users; +GO diff --git a/src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs b/src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs index 0fef57c..f0ead47 100644 --- a/src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs +++ b/src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs @@ -92,10 +92,9 @@ public static class DbInitializer // cho round-robin auto-assign ticket. PHẢI sau SeedDemoUsersAsync (reconcile dept // trước → method này override về IT). Infrastructure data (NOT gated DemoSeed). await SeedItDepartmentStaffAsync(db, userManager, logger); - // [S57bis 2026-06-11] Khóa 14 demo sample user (sếp yêu cầu clear dữ liệu cũ — - // anh chốt scope CHỈ user). PHẢI sau SeedDemoUsers + SeedItDepartmentStaff - // (chạy sau cùng mỗi startup → khóa BỀN, seed fix-drift không resurrect được). - await LockDemoSampleUsersAsync(userManager, logger); + // [S89 2026-06-25 — anh Kiệt FDC chốt] LockDemoSampleUsersAsync ĐÃ GỠ: 16 user mẫu + + // ~21 user UAT-matrix đã XÓA hẳn khỏi prod (scripts/s89-delete-test-users.sql) + gỡ khỏi + // SeedDemoUsersAsync → không còn user test để khóa (S57bis lock thay bằng xóa thật). // Plan B G-H1 (Mig 34 S33 2026-05-26) — seed EmployeeProfile 1-1 với // mọi user @solutions.com.vn. Idempotent. NOT gated DemoSeed flag // (infrastructure data, mirror Mig 32 SeedSampleContractWorkflowV2 @@ -1542,47 +1541,10 @@ public static class DbInitializer // - chuong.phan@solution.com.vn (domain thiếu 's', không trong seed): typo-dup của bản // @solutions nhưng có thể là login thật anh Chương đang dùng — giữ, xác nhận rồi dọn sau. // Muốn mở lại 1 user có chủ đích → gỡ email khỏi list này + admin re-activate. - private static async Task LockDemoSampleUsersAsync( - UserManager userManager, ILogger logger) - { - string[] emails = - [ - // Population Dev — named-person sample seed (16 trừ nv.cao/nv.truong) - "bod.huynh@solutions.com.vn", "bod.le@solutions.com.vn", "bod.tran@solutions.com.vn", - "pm.nguyen@solutions.com.vn", "pm.le@solutions.com.vn", - "ccm.tran@solutions.com.vn", "pro.pham@solutions.com.vn", "fin.do@solutions.com.vn", - "act.vu@solutions.com.vn", "equ.bui@solutions.com.vn", "hra.dang@solutions.com.vn", - "qs.hoang@solutions.com.vn", "qs.ngo@solutions.com.vn", "nv.dinh@solutions.com.vn", - // Population prod — UAT-matrix tạo tay 2026-05-13 (S58) - "act.nv@solutions.com.vn", "act.pp@solutions.com.vn", "act.tp@solutions.com.vn", - "bod.1@solutions.com.vn", "bod.2@solutions.com.vn", - "equ.nv@solutions.com.vn", "equ.pp@solutions.com.vn", "equ.tp@solutions.com.vn", - "fin.nv@solutions.com.vn", "fin.pp@solutions.com.vn", "fin.tp@solutions.com.vn", - "hra.nv@solutions.com.vn", "hra.pp@solutions.com.vn", "hra.tp@solutions.com.vn", - "pm.nv@solutions.com.vn", "pm.pp@solutions.com.vn", "pm.tp@solutions.com.vn", - "qs.nv@solutions.com.vn", "qs.pp@solutions.com.vn", "qs.tp@solutions.com.vn", - ]; - - var locked = 0; - foreach (var email in emails) - { - var user = await userManager.FindByEmailAsync(email); - if (user is null) continue; - if (!user.IsActive && user.LockoutEnd == DateTimeOffset.MaxValue) continue; // đã khóa — idempotent skip - - user.IsActive = false; - user.LockoutEnabled = true; - user.LockoutEnd = DateTimeOffset.MaxValue; - await userManager.UpdateAsync(user); - // Rotate SecurityStamp → vô hiệu refresh-token flow của account bị khóa - // (JWT đang sống tự hết hạn ≤1h theo expiry). - await userManager.UpdateSecurityStampAsync(user); - locked++; - } - - if (locked > 0) - logger.LogInformation("Locked {Count} demo sample users (S57bis clear-old-data)", locked); - } + // [S89 2026-06-25 — anh Kiệt FDC] LockDemoSampleUsersAsync ĐÃ XÓA: 16 user mẫu + ~21 user + // UAT-matrix được XÓA HẲN khỏi prod (scripts/s89-delete-test-users.sql, backup trước) + gỡ + // khỏi SeedDemoUsersAsync. S57bis "khóa demo sample" thay bằng "xóa thật" — chỉ còn 17 user + // Solutions thật (admin + 16 real). Không còn user test để khóa. private static async Task SeedDemoUsersAsync( ApplicationDbContext db, UserManager userManager, ILogger logger) @@ -1600,29 +1562,11 @@ public static class DbInitializer var demoUsers = new[] { // (Email, FullName, DeptCode, Position, RoleNames[]) - // BOD (3) — director + signer + admin assistant - ("bod.huynh@solutions.com.vn", "Huỳnh Văn Hùng", "BOD", "Tổng Giám đốc", new[] { AppRoles.Director }), - ("bod.le@solutions.com.vn", "Lê Thị Mai", "BOD", "Phó Giám đốc (NĐUQ)", new[] { AppRoles.AuthorizedSigner }), - ("bod.tran@solutions.com.vn", "Trần Quốc Bảo", "BOD", "Phó Giám đốc Kỹ thuật (NĐUQ)", new[] { AppRoles.AuthorizedSigner }), - - // PM (2) — giám đốc dự án - ("pm.nguyen@solutions.com.vn", "Nguyễn Quốc Cường", "PM", "Giám đốc Dự án FLOCK 01", new[] { AppRoles.ProjectManager }), - ("pm.le@solutions.com.vn", "Lê Thanh Tùng", "PM", "Giám đốc Dự án Vinhomes Ocean Park", new[] { AppRoles.ProjectManager }), - - // CCM/PRO/FIN/ACT/EQU/HRA — Trưởng phòng các phòng ban (1 mỗi) - ("ccm.tran@solutions.com.vn", "Trần Văn Bình", "CCM", "Trưởng phòng Kiểm soát chi phí", new[] { AppRoles.CostControl, AppRoles.DeptManager }), - ("pro.pham@solutions.com.vn", "Phạm Thị Hồng", "PRO", "Trưởng phòng Cung ứng", new[] { AppRoles.Procurement, AppRoles.DeptManager }), - ("fin.do@solutions.com.vn", "Đỗ Minh Tuấn", "FIN", "Trưởng phòng Tài chính", new[] { AppRoles.Finance, AppRoles.DeptManager }), - ("act.vu@solutions.com.vn", "Vũ Thị Lan", "ACT", "Kế toán trưởng", new[] { AppRoles.Accounting, AppRoles.DeptManager }), - ("equ.bui@solutions.com.vn", "Bùi Văn Khánh", "EQU", "Trưởng phòng Thiết bị", new[] { AppRoles.Equipment, AppRoles.DeptManager }), - ("hra.dang@solutions.com.vn", "Đặng Thị Thanh", "HRA", "Trưởng phòng Nhân sự - Hành chính", new[] { AppRoles.HrAdmin, AppRoles.DeptManager }), - - // Drafter (5) — soạn thảo HĐ ở các phòng khác nhau - ("qs.hoang@solutions.com.vn", "Hoàng Văn Đức", "QS", "QS công trường — soạn thảo HĐ", new[] { AppRoles.Drafter }), - ("qs.ngo@solutions.com.vn", "Ngô Thị Hà", "QS", "QS dự án FLOCK 01", new[] { AppRoles.Drafter }), - ("nv.cao@solutions.com.vn", "Cao Văn Long", "PRO", "Nhân viên Cung ứng", new[] { AppRoles.Drafter }), - ("nv.dinh@solutions.com.vn", "Đinh Thị Yến", "FIN", "Nhân viên Tài chính", new[] { AppRoles.Drafter }), - ("nv.truong@solutions.com.vn", "Trương Minh Quân", "CCM", "Nhân viên Kiểm soát chi phí", new[] { AppRoles.Drafter }), + // [S89 2026-06-25 — anh Kiệt FDC chốt] ĐÃ GỠ 16 user MẪU TEST (tên giả: bod.huynh/le/ + // tran · pm.nguyen/le · ccm.tran · pro.pham · fin.do · act.vu · equ.bui · hra.dang · + // qs.hoang/ngo · nv.cao/dinh/truong) — chỉ giữ user Solutions THẬT bên dưới. Đã xóa + // khỏi prod qua scripts/s89-delete-test-users.sql (kèm ~21 user UAT-matrix tạo tay). + // LockDemoSampleUsersAsync cũng đã gỡ (không còn user test để khóa). // ===== Solutions company users (real names — added 2026-04-25) ===== // PRO — Phòng Cung ứng (5 user)