[CLAUDE] Contract: K3 SP-002 8 workflow nhom KHKK-N1-8 + port level-finalize + designer (0-mig) + 5 test
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m10s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m10s
Seed 8 workflow type-10 theo ROSTER LIVE prod (QT-DN-V2-001, lead verify tay @SQLEXPRESS: 3 tram PRO->CCM->CEO, 11 nguoi, resolve EMAIL, idempotent per-Code; co Finalize CHI cap chuong.phan). Port nhanh level-finalize mirror PE :866-876 (EndedByLevelFinalize K2-de-K3-tieu + ApplyApprovedValuesOnFinalize truoc Phase; opt-out applyLevelFinalize 3-tang interface/service/controller, default true). Sweep ep-false va-3: GO CREATE ep-false Finalize type-10 (OG-9 supersede PHA-VO S155); GIU PUT-Conflict (luat versioning CHUNG - spec liet nham, khai lech); GIU ep-false SkipToFinal type-10 (K3-test O-1 chong config-lie mới) + FE disable o SkipToFinal cho KHKK (gate-K3 F-2). Rao (v) pin-dung-nhom regex KHKK-N[1-8]. +8 MenuKeys AwV2_KhkkN* (All 56->64, policies 224->256 may-dem) + 8 menu-row + resolvePath fe-admin phep-am Khkk_ 0-hit. 5 test K3 (T-a..T-e, anti-tautology). Gate K3 PASS-WITH-FLAGS 8f/0-blocker (sub-reviewer-gate-k3.md): F-1 checkbox opt-out man duyet = KHAI HANDOFF dau-viec-#1 lan sau (tram CEO tam thoi qua Designer bo co); F-2 va; F-3 dong truoc do. Suite 614/0 (45D+569I). K3-BE 2 luot engine-exit -> che nho + lead-solo (bai: task-hep-Write-som song). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -164,6 +164,12 @@ public static class DbInitializer
|
||||
await SeedSampleTravelRequestWorkflowV2Async(db, userManager, logger);
|
||||
await SeedSampleVehicleBookingWorkflowV2Async(db, userManager, logger);
|
||||
|
||||
// [K3 nhịp-1 — S164/S165] 8 quy trình duyệt KHKK theo nhóm duyệt N1..N8
|
||||
// (ApplicableType=ContractSigningPlan=10). INFRASTRUCTURE seed NOT gated
|
||||
// DemoSeed (gotcha #51) — prod cần đủ 8 quy trình ngay deploy đầu, thiếu thì
|
||||
// Drafter không pick được quy trình lúc tạo phiếu KHKK. Idempotent per-Code.
|
||||
await SeedKhkkGroupWorkflowsAsync(db, userManager, logger);
|
||||
|
||||
await WarnDefaultAdminPasswordAsync(userManager, logger);
|
||||
}
|
||||
|
||||
@ -517,6 +523,169 @@ public static class DbInitializer
|
||||
logger.LogInformation("Seeded sample ApprovalWorkflow V2 for VehicleBooking: QT-XE-V2-001 v01");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [K3 nhịp-1 — S164/S165] 8 QUY TRÌNH DUYỆT KHKK theo NHÓM DUYỆT N1..N8
|
||||
// ============================================================================
|
||||
// Danh mục Hợp đồng SOL-CCM-SP-002 (Mig 70) chia 86 dòng vào 8 nhóm duyệt
|
||||
// (`ContractCatalogEntry.ApprovalGroup` 1..8); phiếu KHKK chốt nhóm lúc TẠO
|
||||
// (`ContractSigningPlan.ApprovalGroup`, Mig 71) ⇒ mỗi nhóm cần 1 quy trình V2
|
||||
// riêng để pin. Code `KHKK-N{n}`, ApplicableType=ContractSigningPlan (=10).
|
||||
//
|
||||
// ĐỘI HÌNH: 3 trạm PRO → CCM → BGĐ, sao đúng workflow Duyệt NCC prod bản LIVE
|
||||
// (`QT-DN-V2-001`, ApplicableType=1) theo owner OG-2 — "lấy đúng bộ người duyệt
|
||||
// đang cấu hình ở workflow Duyệt NCC prod". Cấp 1 mỗi trạm = OR-of-N (N row cùng
|
||||
// Order ⇒ bất kỳ ai trong cấp duyệt là xong cấp đó), Cấp 2 = trưởng phòng.
|
||||
//
|
||||
// INFRASTRUCTURE seed — NOT gated DemoSeed (gotcha #51).
|
||||
// Idempotent PER-CODE (`KHKK-N{n}`), CỐ Ý KHÔNG dùng khuôn `AnyAsync(w.Applicable
|
||||
// Type == ...)` của 6 seeder mẫu phía trên: ở đây 8 workflow DÙNG CHUNG 1 type,
|
||||
// check theo type sẽ chặn 7 nhóm còn lại ngay khi nhóm đầu vừa được tạo.
|
||||
//
|
||||
// Resolve người duyệt THEO EMAIL. User vắng mặt (DB dev thiếu người thật) ⇒ BỎ QUA
|
||||
// đúng slot đó + log warning, KHÔNG throw (seeder chạy lúc boot — throw = API chết).
|
||||
// Cấp không còn ai ⇒ bỏ cấp (Order được đánh LẠI liên tục 1,2,… để không vi phạm
|
||||
// ràng buộc sequential của Designer); Bước không còn cấp nào ⇒ bỏ bước; workflow
|
||||
// 0 bước vẫn TẠO (giữ mã cho phiếu pin + admin bổ sung người qua Designer) + log.
|
||||
private static async Task SeedKhkkGroupWorkflowsAsync(
|
||||
ApplicationDbContext db, UserManager<User> userManager, ILogger logger)
|
||||
{
|
||||
// Roster VERIFY LIVE trên prod 2026-07-31 (bản QT-DN-V2-001 IsActive=1) —
|
||||
// bước × phòng × [Cấp 1 OR-of-N] × [Cấp 2] × cờ finalize của Cấp 2.
|
||||
var roster = new (string StepName, string DeptCode, string[] Level1, string[] Level2, bool Level2Finalize)[]
|
||||
{
|
||||
("Phòng Cung Ứng", "PRO",
|
||||
new[]
|
||||
{
|
||||
"phuong.nguyen@solutions.com.vn", // Nguyễn Thị Bích Phượng
|
||||
"long.chau@solutions.com.vn", // Châu Tạ Kim Long
|
||||
"duy.nguyen@solutions.com.vn", // Nguyễn Văn Duy
|
||||
"binh.lethanh@solutions.com.vn", // Bình Lê Thanh (⟂ binh.le bên CCM)
|
||||
},
|
||||
new[] { "tra.bui@solutions.com.vn" }, // Bùi Lê Thủy Trà (TP.PRO)
|
||||
false),
|
||||
|
||||
("P. Kiểm soát Chi phí", "CCM",
|
||||
new[]
|
||||
{
|
||||
"luu.tran@solutions.com.vn", // Trần Xuân Lưu
|
||||
"truong.letrandang@solutions.com.vn", // Lê Trần Đăng Trường (⟂ truong.nguyen bên BGĐ)
|
||||
"binh.le@solutions.com.vn", // Lê Văn Bình
|
||||
"nguyen.ho@solutions.com.vn", // Hồ Thị Nữ Nguyên
|
||||
},
|
||||
new[] { "chuong.phan@solutions.com.vn" }, // Phan Văn Chương (TP.CCM)
|
||||
// 🔴 AllowApproverFinalize=true (owner OG-3/OG-9: a Chương quyết lên CEO hay
|
||||
// kết thúc). Nhánh service port ở nhịp sau CÙNG WAVE trước push nên không
|
||||
// phải config-lie #78.
|
||||
true),
|
||||
|
||||
("Ban Giám đốc", "BOD",
|
||||
new[] { "truong.nguyen@solutions.com.vn" }, // Nguyễn Văn Trường (CEO)
|
||||
Array.Empty<string>(),
|
||||
false),
|
||||
};
|
||||
|
||||
// Resolve email → UserId MỘT lượt (11 email, dùng lại cho cả 8 workflow).
|
||||
var usersByEmail = new Dictionary<string, Guid>(StringComparer.OrdinalIgnoreCase);
|
||||
var missingEmails = new List<string>();
|
||||
foreach (var email in roster.SelectMany(r => r.Level1.Concat(r.Level2))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var user = await userManager.FindByEmailAsync(email);
|
||||
if (user is null) { missingEmails.Add(email); continue; }
|
||||
usersByEmail[email] = user.Id;
|
||||
}
|
||||
if (missingEmails.Count > 0)
|
||||
logger.LogWarning(
|
||||
"SeedKhkkGroupWorkflowsAsync: {Count} email người duyệt KHÔNG có user — bỏ qua slot đó: {Emails}",
|
||||
missingEmails.Count, string.Join(", ", missingEmails));
|
||||
|
||||
var deptIdByCode = (await db.Departments.ToListAsync())
|
||||
.GroupBy(d => d.Code)
|
||||
.ToDictionary(g => g.Key, g => (Guid?)g.First().Id);
|
||||
|
||||
var seeded = 0;
|
||||
for (var n = 1; n <= 8; n++)
|
||||
{
|
||||
var code = $"KHKK-N{n}";
|
||||
if (await db.ApprovalWorkflows.AnyAsync(w => w.Code == code)) continue;
|
||||
|
||||
var wf = new ApprovalWorkflow
|
||||
{
|
||||
Code = code,
|
||||
Version = 1,
|
||||
ApplicableType = ApprovalWorkflowApplicableType.ContractSigningPlan,
|
||||
Name = $"Duyệt KHKK — Nhóm {n}",
|
||||
Description =
|
||||
$"Quy trình duyệt Kế hoạch ký kết HĐ cho nhóm duyệt N{n} (danh mục SOL-CCM-SP-002). " +
|
||||
"3 trạm PRO → CCM → BGĐ, đội hình sao từ workflow Duyệt NCC prod. " +
|
||||
"Admin có thể chỉnh người/quyền tại chỗ hoặc tạo version mới qua Designer.",
|
||||
IsActive = true,
|
||||
IsUserSelectable = true,
|
||||
ActivatedAt = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var stepOrder = 1;
|
||||
foreach (var r in roster)
|
||||
{
|
||||
var step = new ApprovalWorkflowStep
|
||||
{
|
||||
ApprovalWorkflow = wf,
|
||||
Order = stepOrder,
|
||||
Name = r.StepName,
|
||||
DepartmentId = deptIdByCode.GetValueOrDefault(r.DeptCode),
|
||||
};
|
||||
|
||||
var levelOrder = 1;
|
||||
foreach (var (emails, finalize) in new[] { (r.Level1, false), (r.Level2, r.Level2Finalize) })
|
||||
{
|
||||
var approverIds = emails
|
||||
.Where(usersByEmail.ContainsKey)
|
||||
.Select(e => usersByEmail[e])
|
||||
.ToArray();
|
||||
if (approverIds.Length == 0) continue; // cấp rỗng → bỏ, KHÔNG chừa lỗ Order
|
||||
|
||||
foreach (var approverId in approverIds)
|
||||
{
|
||||
step.Levels.Add(new ApprovalWorkflowLevel
|
||||
{
|
||||
Step = step,
|
||||
Order = levelOrder, // N row cùng Order = OR-of-N trong 1 Cấp
|
||||
Name = $"Cấp {levelOrder}",
|
||||
ApproverUserId = approverId,
|
||||
AllowApproverFinalize = finalize,
|
||||
});
|
||||
}
|
||||
levelOrder++;
|
||||
}
|
||||
|
||||
if (step.Levels.Count == 0)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"SeedKhkkGroupWorkflowsAsync: {Code} bỏ Bước \"{Step}\" — 0 người duyệt resolve được",
|
||||
code, r.StepName);
|
||||
continue;
|
||||
}
|
||||
|
||||
wf.Steps.Add(step);
|
||||
stepOrder++;
|
||||
}
|
||||
|
||||
if (wf.Steps.Count == 0)
|
||||
logger.LogWarning(
|
||||
"SeedKhkkGroupWorkflowsAsync: {Code} tạo RỖNG (0 Bước có người) — admin phải thêm người qua Designer",
|
||||
code);
|
||||
|
||||
db.ApprovalWorkflows.Add(wf);
|
||||
seeded++;
|
||||
}
|
||||
|
||||
if (seeded > 0)
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
logger.LogInformation("Seeded {Count} KHKK group workflows (K3 SP-002)", seeded);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -1808,6 +1977,18 @@ public static class DbInitializer
|
||||
(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"),
|
||||
// [K3 nhịp-1 — S164/S165] 8 leaf Designer cho 8 nhóm duyệt KHKK (workflow
|
||||
// `KHKK-N{n}` type-10 seed ở SeedKhkkGroupWorkflowsAsync). Key CÓ trong
|
||||
// MenuKeys.All ⇒ Admin được grant tự động qua loop SeedAdminPermissionsAsync;
|
||||
// upsert dưới là idempotent per-Key (key đã có → chỉ re-set Order rồi continue).
|
||||
(MenuKeys.ApprovalWorkflowKhkkN1, "Duyệt KHKK — Nhóm 1", MenuKeys.ApprovalWorkflowsV2, 3, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN2, "Duyệt KHKK — Nhóm 2", MenuKeys.ApprovalWorkflowsV2, 4, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN3, "Duyệt KHKK — Nhóm 3", MenuKeys.ApprovalWorkflowsV2, 5, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN4, "Duyệt KHKK — Nhóm 4", MenuKeys.ApprovalWorkflowsV2, 6, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN5, "Duyệt KHKK — Nhóm 5", MenuKeys.ApprovalWorkflowsV2, 7, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN6, "Duyệt KHKK — Nhóm 6", MenuKeys.ApprovalWorkflowsV2, 8, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN7, "Duyệt KHKK — Nhóm 7", MenuKeys.ApprovalWorkflowsV2, 9, "FileSignature"),
|
||||
(MenuKeys.ApprovalWorkflowKhkkN8, "Duyệt KHKK — Nhóm 8", MenuKeys.ApprovalWorkflowsV2, 10, "FileSignature"),
|
||||
// [S61 Mig 50] Menu module Ngân sách cũ (Budgets + 3 leaf Bg_*) đã XÓA —
|
||||
// rows cũ trên DB cleanup qua SQL trong migration (idempotent DELETE).
|
||||
// Module Nhân sự (Phase 10.1 G-H1 — Mig 34 S33). Root operational HR.
|
||||
|
||||
Reference in New Issue
Block a user