[CLAUDE] Infra: tạm ẩn HRM/Văn phòng số/Cá nhân khỏi user thường + Danh mục xuống cuối sidebar (S58)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m24s

Anh yêu cầu (screenshot eoffice): ẩn các tính năng chưa golive + thu hồi
phân quyền user thường; nhóm Danh mục đưa xuống cuối.

- NEW RevokeTemporarilyHiddenModulesAsync (chạy cuối seed pipeline): set 4 cờ
  CRUD=false cho MỌI role TRỪ Admin trên keys Hrm* (gồm Hrm_Config*) + Off*
  (gồm Off_ChamCong thuộc Cá nhân) + Personal. Giữ row (không xóa) — flip lại
  nhanh khi golive. Menu tự ẩn cả 2 app (GetMyMenuTree lọc CanRead).
- SeedAllRolesReviewReadPermissionsAsync: thu hẹp grant scope còn
  Master/Catalogs/Pe_* (bỏ Hrm/Off/Personal — không re-grant cái vừa revoke).
- Menu "Danh mục" (Master) Order 20→80 — cuối vùng nghiệp vụ, trước System 90.
  Main upsert tự re-set Order trên DB cũ (idempotent sẵn).
- KHÔNG đụng: Pe_* (sếp cần all-role) + Master/Catalogs CanRead (flow PE cần
  xem master data) + Admin (quản trị) + IsVisible layer (Menu eOffice toggle).

Runtime-proof Dev: MasterOrder=80 · NonAdminHrmOffPersonalCanRead=0 ·
AdminHrmOffCanRead=28 · NonAdminPeCanCreate=120 · NonAdminMasterCanRead=48.
Build 0/0, test 240 PASS. Lưu ý mức che: menu + permission matrix; URL gõ
trực tiếp chưa chặn (FE không PermissionGuard per-route — chấp nhận "tạm ẩn").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-11 13:36:08 +07:00
parent 2aefb3134d
commit 6c5fd26428

View File

@ -1787,7 +1787,10 @@ public static class DbInitializer
var tree = new List<(string Key, string Label, string? Parent, int Order, string Icon)>
{
(MenuKeys.Dashboard, "Tổng quan", null, 10, "LayoutDashboard"),
(MenuKeys.Master, "Danh mục", null, 20, "Database"),
// [S58 2026-06-11] "Danh mục" 20→80: anh yêu cầu đưa xuống CUỐI sidebar
// (sau Reports 50, trước vùng System 90 admin). Main upsert tự re-set
// Order trên DB cũ. Con (21-25) giữ nguyên — order chỉ so trong siblings.
(MenuKeys.Master, "Danh mục", null, 80, "Database"),
(MenuKeys.Suppliers, "Nhà cung cấp", MenuKeys.Master, 21, "Building2"),
(MenuKeys.Projects, "Dự án", MenuKeys.Master, 22, "FolderKanban"),
(MenuKeys.Departments, "Phòng ban", MenuKeys.Master, 23, "Users"),
@ -2050,7 +2053,15 @@ public static class DbInitializer
// [S57] Mở quyền XEM (Read-only) cho TẤT CẢ role để mọi bộ phận review/góp ý
// các module HRM + Văn phòng số + Danh mục (master). KHÔNG đụng Duyệt NCC
// (Pe_*/PeWf_*/AwV2 — sắp go-live, giữ phân quyền cũ), Contracts/Budgets/System.
// [S58] Scope grant THU HẸP còn Master/Catalogs/Pe_* — xem note trong method.
await SeedAllRolesReviewReadPermissionsAsync(db, roleManager, logger);
// [S58 2026-06-11] TẠM ẨN module chưa golive với user thường (anh yêu cầu,
// screenshot eoffice): thu hồi quyền HRM (Hrm*) + Văn phòng số (Off*) +
// Cá nhân (Personal) khỏi MỌI role TRỪ Admin. Menu tự ẩn cả 2 app
// (GetMyMenuTree lọc CanRead). Chạy SAU grant seed để revoke thắng.
// Mở lại sau golive: gỡ prefix khỏi revoke + thêm lại vào InReviewScope.
await RevokeTemporarilyHiddenModulesAsync(db, roleManager, logger);
}
// [S57] Cấp CanRead (CHỈ xem) cho MỌI role trên menu HRM + Office + Master để mọi
@ -2068,12 +2079,13 @@ public static class DbInitializer
private static async Task SeedAllRolesReviewReadPermissionsAsync(
ApplicationDbContext db, RoleManager<Role> roleManager, ILogger logger)
{
// Scope read-only = HRM (Hrm*) + Office (Off*) + Personal + Master + Catalogs.
// Scope grant = Master + Catalogs + Pe_*.
// [S57bis] +Pe_* (Duyệt NCC) — semantics riêng read+create xử lý bên dưới.
// Loại trừ tự nhiên (không match prefix): PeWf_* (4th char 'W' ≠ '_'),
// AwV2_*, Ct_*, Bg_*, Wf_*, System keys.
// [S58] BỎ Hrm*/Off*/Personal khỏi grant — sếp yêu cầu TẠM ẨN HRM + Văn phòng
// số + Cá nhân với user thường (chưa golive các module này). Revoke tương ứng
// ở RevokeTemporarilyHiddenModulesAsync (chạy sau). Loại trừ tự nhiên (không
// match prefix): PeWf_* (ký tự thứ 3 'W' ≠ '_'), AwV2_*, Ct_*, Bg_*, Wf_*, System.
static bool InReviewScope(string key) =>
key.StartsWith("Hrm") || key.StartsWith("Off") || key == MenuKeys.Personal ||
key.StartsWith("Catalog") || key == MenuKeys.Master ||
key == MenuKeys.Suppliers || key == MenuKeys.Projects || key == MenuKeys.Departments ||
key.StartsWith("Pe_");
@ -2151,6 +2163,50 @@ public static class DbInitializer
}
}
// [S58 2026-06-11] Thu hồi quyền các module TẠM ẨN (chưa golive — anh yêu cầu từ
// screenshot eoffice) khỏi mọi role non-Admin: HRM (Hrm* gồm Hrm_Config*) +
// Văn phòng số (Off* gồm Off_ChamCong đã re-parent về Cá nhân) + root Cá nhân
// (Personal). Set cả 4 cờ CRUD=false (KHÔNG xóa row — giữ vết, flip lại nhanh
// khi golive). Admin GIỮ nguyên để quản trị/chuẩn bị dữ liệu.
// Lưu ý mức che: ẩn menu (GetMyMenuTree lọc CanRead) + permission matrix; FE
// KHÔNG có PermissionGuard per-route nên gõ URL trực tiếp vẫn render trang
// (API self-service vẫn [Authorize] thường) — chấp nhận cho mức "tạm ẩn".
// Idempotent: lần 2 không row nào còn cờ true → 0 update.
private static async Task RevokeTemporarilyHiddenModulesAsync(
ApplicationDbContext db, RoleManager<Role> roleManager, ILogger logger)
{
var adminRole = await roleManager.FindByNameAsync(AppRoles.Admin);
if (adminRole is null)
{
logger.LogWarning("RevokeTemporarilyHiddenModulesAsync: skip — Admin role chưa seed.");
return;
}
var adminRoleId = adminRole.Id;
var rows = await db.Permissions
.Where(p => p.RoleId != adminRoleId
&& (p.MenuKey.StartsWith("Hrm") || p.MenuKey.StartsWith("Off")
|| p.MenuKey == MenuKeys.Personal)
&& (p.CanRead || p.CanCreate || p.CanUpdate || p.CanDelete))
.ToListAsync();
foreach (var row in rows)
{
row.CanRead = false;
row.CanCreate = false;
row.CanUpdate = false;
row.CanDelete = false;
}
if (rows.Count > 0)
{
await db.SaveChangesAsync();
logger.LogInformation(
"Revoked {Count} permission rows — tạm ẩn HRM/Văn phòng số/Cá nhân khỏi non-Admin (S58)",
rows.Count);
}
}
// [Plan CA S29 2026-05-22] Permission defaults cho role CatalogManager.
// Strategy: full CRUD trên 9 menu key danh mục dùng chung:
// - Master (root group) + Suppliers + Projects + Departments