[CLAUDE] Workflow V2: clone leaf Designer + sample seed cho DuyetNccPhuongAn (B)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m16s

Mở rộng V2 schema cho type B mirror type A đã chốt S17. Phần lớn đã chung
qua ApplicableType discriminator — chỉ thêm menu key + sample seed.

Changes:
- MenuKeys.cs: +const ApprovalWorkflowDuyetNccPhuongAnV2 (AwV2_DuyetNccPhuongAn) + add vào All array
- DbInitializer.SeedMenusAsync: +leaf "Duyệt NCC và Giải pháp (Mới)" dưới root ApprovalWorkflowsV2
- DbInitializer +SeedSampleApprovalWorkflowsV2Async: seed QT-DN-PA-V2-001 v01 (1 Bước Phòng CCM × 1 Cấp NV test)
  Idempotent — skip nếu admin đã tạo bất kỳ workflow B nào hoặc thiếu test user
- fe-admin/lib/menuKeys.ts: +AwV2_DuyetNccPhuongAn

KHÔNG đụng:
- Migration (V2 schema chung qua ApplicableType — Mig 22-24 đã hỗ trợ B)
- Service ApproveV2Async (không hardcode type)
- Designer page ApprovalWorkflowsV2Page (TYPE_CODE_TO_INT đã có B=2)
- Layout/App.tsx (regex AwV2_(.+) match dynamic)
- Permission default (admin bypass + role khác không cần Designer access)

Verify: dotnet build pass · 81 test pass · npm build fe-admin pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-08 18:07:56 +07:00
parent 917446dbeb
commit 937eb2449c
3 changed files with 59 additions and 3 deletions

View File

@ -17,6 +17,7 @@ export const MenuKeys = {
// Quy trình duyệt MỚI (Mig 22 — Session 17, 2026-05-08)
ApprovalWorkflowsV2: 'ApprovalWorkflowsV2',
AwV2_DuyetNcc: 'AwV2_DuyetNcc',
AwV2_DuyetNccPhuongAn: 'AwV2_DuyetNccPhuongAn',
} as const
export type MenuKey = typeof MenuKeys[keyof typeof MenuKeys]

View File

@ -64,6 +64,7 @@ public static class MenuKeys
// ============================================================
public const string ApprovalWorkflowsV2 = "ApprovalWorkflowsV2"; // root admin (mới)
public const string ApprovalWorkflowDuyetNccV2 = "AwV2_DuyetNcc"; // leaf cho Duyệt NCC mới
public const string ApprovalWorkflowDuyetNccPhuongAnV2 = "AwV2_DuyetNccPhuongAn"; // leaf cho Duyệt NCC và Giải pháp mới
// ============================================================
// Module Ngân sách (Phase 7) — 4 bảng quản lý ngân sách dự án/gói thầu.
@ -95,7 +96,7 @@ public static class MenuKeys
PurchaseEvaluations,
Budgets, BudgetList, BudgetCreate, BudgetPending,
System, Users, Roles, Permissions, Workflows, PeWorkflows,
ApprovalWorkflowsV2, ApprovalWorkflowDuyetNccV2, // Mig 22
ApprovalWorkflowsV2, ApprovalWorkflowDuyetNccV2, ApprovalWorkflowDuyetNccPhuongAnV2, // Mig 22
];
public static readonly string[] Actions = ["Read", "Create", "Update", "Delete"];

View File

@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SolutionErp.Application.Contracts.Services;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Forms;
using SolutionErp.Domain.Identity;
@ -81,10 +82,62 @@ public static class DbInitializer
await SeedDemoContractsAsync(db, userManager, codeGen, logger);
await SeedDemoPurchaseEvaluationsAsync(db, userManager, logger);
await SeedSampleApprovalWorkflowsV2Async(db, userManager, logger);
await WarnDefaultAdminPasswordAsync(userManager, logger);
}
// Seed sample workflow V2 cho ApplicableType=DuyetNccPhuongAn (B), giúp UAT
// type B nhanh không cần admin tạo qua Designer trước. Idempotent — skip nếu
// đã có ANY workflow B (admin đã tạo) HOẶC nếu thiếu test user / phòng CCM.
// KHÔNG seed cho A (admin đã tạo + chốt qua Designer S17 — không clobber).
private static async Task SeedSampleApprovalWorkflowsV2Async(
ApplicationDbContext db, UserManager<User> userManager, ILogger logger)
{
var hasAnyB = await db.ApprovalWorkflows
.AnyAsync(w => w.ApplicableType == ApprovalWorkflowApplicableType.DuyetNccPhuongAn);
if (hasAnyB) return;
var testUser = await userManager.FindByEmailAsync("nv.test@solutions.com.vn");
if (testUser is null)
{
logger.LogWarning("SeedSampleApprovalWorkflowsV2Async: skip — test user nv.test@solutions.com.vn not found");
return;
}
var ccmDept = await db.Departments.FirstOrDefaultAsync(d => d.Code == "CCM");
var wf = new ApprovalWorkflow
{
Code = "QT-DN-PA-V2-001",
Version = 1,
ApplicableType = ApprovalWorkflowApplicableType.DuyetNccPhuongAn,
Name = "Quy trình duyệt NCC và Giải pháp (mẫu UAT)",
Description = "Sample seed cho UAT B — 1 Bước Phòng CCM × 1 Cấp NV test. Admin có thể clone tạo version mới qua Designer.",
IsActive = true,
ActivatedAt = DateTime.UtcNow,
};
var step = new ApprovalWorkflowStep
{
ApprovalWorkflow = wf,
Order = 1,
Name = "Phòng CCM",
DepartmentId = ccmDept?.Id,
};
var level = new ApprovalWorkflowLevel
{
Step = step,
Order = 1,
Name = "Cấp 1",
ApproverUserId = testUser.Id,
};
wf.Steps.Add(step);
step.Levels.Add(level);
db.ApprovalWorkflows.Add(wf);
await db.SaveChangesAsync();
logger.LogInformation("Seeded sample ApprovalWorkflow V2 for DuyetNccPhuongAn: QT-DN-PA-V2-001 v01");
}
// Seed 4 master catalogs với defaults cho user nhập liệu Details. Idempotent:
// skip per-table nếu đã có row (admin có thể đã thêm/sửa — không clobber).
private static async Task SeedCatalogsAsync(ApplicationDbContext db, ILogger logger)
@ -1312,6 +1365,7 @@ public static class DbInitializer
// UAT trước khi migrate hoàn toàn. Cấu trúc: Quy trình > Bước (Phòng) > Cấp (NV cụ thể).
(MenuKeys.ApprovalWorkflowsV2, "Quy trình duyệt (Mới)", MenuKeys.System, 96, "Workflow"),
(MenuKeys.ApprovalWorkflowDuyetNccV2, "Duyệt NCC (Mới)", MenuKeys.ApprovalWorkflowsV2, 1, "FileCheck"),
(MenuKeys.ApprovalWorkflowDuyetNccPhuongAnV2, "Duyệt NCC và Giải pháp (Mới)", MenuKeys.ApprovalWorkflowsV2, 2, "FileCheck"),
// Module Ngân sách (Phase 7)
(MenuKeys.Budgets, "Ngân sách", null, 27, "Wallet"),
(MenuKeys.BudgetList, "Danh sách", MenuKeys.Budgets, 1, "List"),