[CLAUDE] PurchaseEvaluation: PE gắn Hạng mục công việc (Mig 49) + mở quyền Pe all-role + menu Cá nhân + khóa 14 demo user
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m24s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m24s
Sếp chốt deadline 15:00 (Zalo 11:02-11:17): flow tạo phiếu chọn quy trình → dự án → HẠNG MỤC → NCC/TP; phiếu dạng «Dự án – Hạng mục»; all-user thấy Duyệt NCC + master config; clear data cũ. - Mig 49 AddWorkItemToPurchaseEvaluation: PE.WorkItemId Guid? loose-Guid + index (KHÔNG FK vật lý — convention PE, database-agent design). Validator NotEmpty (create) + FK-guard AnyAsync(IsActive) → Conflict + UpdateDraft NULL-SAFE (client không gửi → giữ, chống null-hóa bug-class S42). 3 projection ListItemDto LEFT-join WorkItems. - FE ×2 app: PeWorkspaceCreateView select «c. Hạng mục *» + PeHeaderForm (load existing + PUT gửi lại, SHA256 IDENTICAL) + PeDetailTabs (header «Dự án – Hạng mục» + FormRow + inline khóa) + types. Route reuse /catalogs/work-items. - Perm: SeedAllRolesReviewReadPermissionsAsync extend Pe_* 11 key (factory — Pe leaf không nằm All) CanRead+CanCreate upgrade-only mọi role; PeWf_*/AwV2 GIỮ Admin. HRM/Office/Master/Catalogs CanRead (S57). Master write-lock Admin,CatalogManager ×3 controller. - Menu «Cá nhân» (Personal root 30, mirror Puro) + Chấm công re-parent + HrmConfig→Master + parentBackfill idempotent + admin bỏ ẩn Master (đảo S29). - LockDemoSampleUsersAsync: khóa 14/16 sample (GIỮ nv.cao+nv.truong IT-pool + catalog.manager) — ungated idempotent, IsActive=0+Lockout+SecurityStamp rotate. - Tests +12 PeWorkItemGuardTests (validator/FK-guard/null-safe) → 240 PASS. npm ×2 + BE 0W/0E. - Excel (3) đối chiếu: 62/71/3 identical S55 — no data change. - Gate: em main evidence-checklist (2 reviewer-spawn die-0-byte — resume-kill; backstop 12 guard-test + authz-key/role-string/Mig-49 evidence-lệnh). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -25,6 +25,9 @@ public class PurchaseEvaluationConfiguration : IEntityTypeConfiguration<Purchase
|
||||
b.HasIndex(x => x.MaPhieu).IsUnique().HasFilter("[MaPhieu] IS NOT NULL");
|
||||
b.HasIndex(x => new { x.Phase, x.IsDeleted });
|
||||
b.HasIndex(x => x.ProjectId);
|
||||
// [Mig 49 S57bis] WorkItemId scalar loose-Guid — index lọc query, KHÔNG
|
||||
// HasOne/FK vật lý (convention PE: chỉ ApprovalWorkflowId có FK).
|
||||
b.HasIndex(x => x.WorkItemId);
|
||||
b.HasIndex(x => x.SlaDeadline);
|
||||
b.HasIndex(x => x.WorkflowDefinitionId);
|
||||
b.HasIndex(x => x.ApprovalWorkflowId);
|
||||
|
||||
@ -92,6 +92,10 @@ 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);
|
||||
// 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
|
||||
@ -1537,6 +1541,47 @@ public static class DbInitializer
|
||||
// Default password: User@123456 (warn log để rotate prod).
|
||||
private const string DemoUserPassword = "User@123456";
|
||||
|
||||
// [S57bis 2026-06-11] Khóa 14/16 demo sample user — sếp yêu cầu "clear dữ liệu cũ",
|
||||
// anh chốt scope: CHỈ khóa user demo (GIỮ phiếu/HĐ/NCC/dự án demo). Ungated idempotent
|
||||
// (mirror SeedRealMasterDataAsync philosophy): chạy mọi startup → khóa BỀN kể cả ai
|
||||
// re-activate nhầm. GIỮ ACTIVE có chủ đích:
|
||||
// - nv.cao + nv.truong : IT helpdesk round-robin pool (S52 P11-D) — khóa nốt SAU KHI
|
||||
// anh gán ≥1 user thật vào Phòng CNTT (ops-pending S56), tránh helpdesk chết hẳn.
|
||||
// - catalog.manager : account chức năng quản danh mục dùng chung (Plan CA S29).
|
||||
// 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<User> userManager, ILogger logger)
|
||||
{
|
||||
string[] emails =
|
||||
[
|
||||
"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",
|
||||
];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static async Task SeedDemoUsersAsync(
|
||||
ApplicationDbContext db, UserManager<User> userManager, ILogger logger)
|
||||
{
|
||||
@ -1729,7 +1774,8 @@ public static class DbInitializer
|
||||
(MenuKeys.CatalogMaterials,"Vật tư / SP", MenuKeys.Catalogs, 2, "Package"),
|
||||
(MenuKeys.CatalogServices, "Dịch vụ", MenuKeys.Catalogs, 3, "Wrench"),
|
||||
(MenuKeys.CatalogWorkItems,"Hạng mục công việc", MenuKeys.Catalogs, 4, "ListChecks"),
|
||||
(MenuKeys.Contracts, "Hợp đồng", null, 30, "FileText"),
|
||||
// [S57] Order 30→31: nhường slot 30 cho nhóm "Cá nhân" (đứng ngay sau Văn phòng số = 29, mirror Puro).
|
||||
(MenuKeys.Contracts, "Hợp đồng", null, 31, "FileText"),
|
||||
(MenuKeys.Forms, "Biểu mẫu", null, 40, "FileSpreadsheet"),
|
||||
(MenuKeys.Reports, "Báo cáo", null, 50, "BarChart3"),
|
||||
(MenuKeys.System, "Hệ thống", null, 90, "Settings"),
|
||||
@ -1751,13 +1797,17 @@ public static class DbInitializer
|
||||
(MenuKeys.BudgetList, "Danh sách", MenuKeys.Budgets, 1, "List"),
|
||||
(MenuKeys.BudgetCreate, "Thao tác", MenuKeys.Budgets, 2, "Plus"),
|
||||
(MenuKeys.BudgetPending, "Duyệt", MenuKeys.Budgets, 3, "CheckCircle2"),
|
||||
// Module Nhân sự (Phase 10.1 G-H1 — Mig 34 S33). 1 root + 1 leaf
|
||||
// Phase 1 minimal. Phase 1.5 + G-H2/G-H3 thêm Config/Dashboard.
|
||||
// Module Nhân sự (Phase 10.1 G-H1 — Mig 34 S33). Root operational HR.
|
||||
// [S57] "Cấu hình HRM" re-parent sang "Danh mục" (Master) — gom config 1 chỗ.
|
||||
// Hrm còn: Dashboard(1) → Hồ sơ(2), Dashboard đầu nhóm (khớp Puro).
|
||||
(MenuKeys.Hrm, "Nhân sự", null, 28, "UserCircle"),
|
||||
(MenuKeys.HrmHoSo, "Hồ sơ Nhân sự", MenuKeys.Hrm, 1, "ContactRound"),
|
||||
(MenuKeys.HrmHoSo, "Hồ sơ Nhân sự", MenuKeys.Hrm, 2, "ContactRound"),
|
||||
|
||||
// Phase 10.2 G-H2 (Mig 35 — S34). Sub-group "Cấu hình HRM" + 4 catalog leaf.
|
||||
(MenuKeys.HrmConfig, "Cấu hình HRM", MenuKeys.Hrm, 2, "Settings2"),
|
||||
// Phase 10.2 G-H2 (Mig 35 — S34). Sub-group "Cấu hình HRM" + 6 catalog leaf.
|
||||
// [S57] parent Hrm → Master: nằm dưới "Danh mục" (order 25, sau Catalogs) để gom
|
||||
// toàn bộ config/catalog 1 chỗ. 6 leaf bên dưới giữ parent=HrmConfig nên theo cùng.
|
||||
// DB cũ propagate qua parentBackfill bên dưới (main upsert chỉ re-set Order).
|
||||
(MenuKeys.HrmConfig, "Cấu hình HRM", MenuKeys.Master, 25, "Settings2"),
|
||||
(MenuKeys.HrmConfigLeaveTypes, "Loại phép", MenuKeys.HrmConfig, 1, "CalendarOff"),
|
||||
(MenuKeys.HrmConfigHolidays, "Ngày lễ", MenuKeys.HrmConfig, 2, "PartyPopper"),
|
||||
(MenuKeys.HrmConfigShifts, "Ca làm việc", MenuKeys.HrmConfig, 3, "Clock"),
|
||||
@ -1787,10 +1837,15 @@ public static class DbInitializer
|
||||
(MenuKeys.OffDonTuTravel, "Công tác", MenuKeys.OffDonTu, 3, "Plane"),
|
||||
(MenuKeys.OffDatXe, "Đặt xe công", MenuKeys.Off, 5, "Car"),
|
||||
(MenuKeys.OffItTicket, "Ticket CNTT", MenuKeys.Off, 6, "Ticket"),
|
||||
(MenuKeys.OffChamCong, "Chấm công", MenuKeys.Off, 7, "Fingerprint"),
|
||||
(MenuKeys.OffAttendanceReport, "Báo cáo chấm công", MenuKeys.Off, 8, "FileBarChart"),
|
||||
// Phase 10.4 G-H3 — Dashboard NS dưới root Hrm.
|
||||
(MenuKeys.HrmDashboard, "Dashboard NS", MenuKeys.Hrm, 3, "BarChart3"),
|
||||
// [S57] "Báo cáo chấm công" giữ ở Văn phòng số (báo cáo admin, order 7 — lấp chỗ Chấm công rời đi).
|
||||
(MenuKeys.OffAttendanceReport, "Báo cáo chấm công", MenuKeys.Off, 7, "FileBarChart"),
|
||||
// [S57] Nhóm "Cá nhân" (mirror Puro). Root order 30 = ngay sau Văn phòng số (29).
|
||||
// "Chấm công" re-parent Off → Personal; với DB cũ propagate qua parentBackfill bên dưới
|
||||
// (main upsert chỉ re-set Order, KHÔNG đụng ParentKey).
|
||||
(MenuKeys.Personal, "Cá nhân", null, 30, "UserRound"),
|
||||
(MenuKeys.OffChamCong, "Chấm công", MenuKeys.Personal, 1, "Fingerprint"),
|
||||
// Phase 10.4 G-H3 — Dashboard NS dưới root Hrm. [S57] Order 1 = đầu nhóm (khớp Puro).
|
||||
(MenuKeys.HrmDashboard, "Dashboard NS", MenuKeys.Hrm, 1, "BarChart3"),
|
||||
};
|
||||
|
||||
// Per-type sub-menu under Contracts: 1 group + 3 leaves each
|
||||
@ -1895,6 +1950,30 @@ public static class DbInitializer
|
||||
logger.LogInformation("Backfilled {Count} menu labels", updatedLabels);
|
||||
}
|
||||
|
||||
// [S57] Re-parent backfill — chuyển node sang group khác trên DB cũ. Main
|
||||
// upsert phía trên CHỈ re-set Order, KHÔNG đụng ParentKey (xem comment trên),
|
||||
// nên đổi nhóm phải update ParentKey riêng. Idempotent. "Chấm công" Off → Cá nhân.
|
||||
var parentBackfill = new Dictionary<string, string?>
|
||||
{
|
||||
[MenuKeys.OffChamCong] = MenuKeys.Personal, // [S57] Chấm công → Cá nhân
|
||||
[MenuKeys.HrmConfig] = MenuKeys.Master, // [S57] Cấu hình HRM → Danh mục (gom config 1 chỗ)
|
||||
};
|
||||
var reparented = 0;
|
||||
foreach (var (key, expectedParent) in parentBackfill)
|
||||
{
|
||||
var item = await db.MenuItems.FirstOrDefaultAsync(m => m.Key == key);
|
||||
if (item != null && item.ParentKey != expectedParent)
|
||||
{
|
||||
item.ParentKey = expectedParent;
|
||||
reparented++;
|
||||
}
|
||||
}
|
||||
if (reparented > 0)
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
logger.LogInformation("Re-parented {Count} menu items", reparented);
|
||||
}
|
||||
|
||||
// Backfill WorkflowDefinition name cho B (Phương Án → Giải pháp rename).
|
||||
var wfB = await db.PurchaseEvaluationWorkflowDefinitions
|
||||
.FirstOrDefaultAsync(w => w.Code == "QT-DN-B" && w.Version == 1);
|
||||
@ -1944,6 +2023,109 @@ public static class DbInitializer
|
||||
// (Master/Suppliers/Projects/Departments + 4 Catalogs leaf). Admin gán role
|
||||
// cho user nào cần CRUD danh mục sau khi move FE từ admin → eoffice.
|
||||
await SeedCatalogManagerPermissionsAsync(db, roleManager, logger);
|
||||
|
||||
// [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.
|
||||
await SeedAllRolesReviewReadPermissionsAsync(db, roleManager, logger);
|
||||
}
|
||||
|
||||
// [S57] Cấp CanRead (CHỈ xem) cho MỌI role trên menu HRM + Office + Master để mọi
|
||||
// bộ phận nhân viên thấy + review/góp ý. Additive idempotent → KHÔNG xóa quyền
|
||||
// sẵn có. Write vẫn khóa ở controller (Master: Admin+CatalogManager;
|
||||
// HRM-config/Catalogs/MeetingRoom: Admin).
|
||||
//
|
||||
// [S57bis] Mở Duyệt NCC (Pe_*) cho MỌI role: anh chốt "Xem + Tạo".
|
||||
// - Key HRM/Office/Master/Catalogs : CanRead-only, skip-existing (giữ nguyên).
|
||||
// - Key Pe_* : CanRead=true + CanCreate=true.
|
||||
// Idempotent UPGRADE-ONLY: row Pe_* đã tồn tại (Pe defaults cũ seed 7 role)
|
||||
// mà CanRead HOẶC CanCreate=false → NÂNG đúng 2 cờ đó lên true. KHÔNG hạ +
|
||||
// KHÔNG đụng CanUpdate/CanDelete (additive — không phá quyền admin đã chỉnh
|
||||
// cao hơn). Row chưa có → tạo mới CanRead+CanCreate=true, Update/Delete=false.
|
||||
private static async Task SeedAllRolesReviewReadPermissionsAsync(
|
||||
ApplicationDbContext db, RoleManager<Role> roleManager, ILogger logger)
|
||||
{
|
||||
// Scope read-only = HRM (Hrm*) + Office (Off*) + Personal + Master + Catalogs.
|
||||
// [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.
|
||||
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_");
|
||||
|
||||
// Phân biệt key Pe_* (read+create) vs read-only. Pe_* match cờ thứ-3 '_'
|
||||
// → "PeWf_*"/"PeWorkflows" KHÔNG match (loại admin Designer).
|
||||
static bool IsPeKey(string key) => key.StartsWith("Pe_");
|
||||
|
||||
// MenuKeys.All chứa root PurchaseEvaluations nhưng KHÔNG chứa Pe_* leaf
|
||||
// (sinh động qua factory). Build leaf giống SeedPurchaseEvaluationPermissionDefaultsAsync
|
||||
// để upgrade đúng row Pe_* thật trong DB (1 root + 5 leaf × 2 type).
|
||||
var peKeys = new List<string> { MenuKeys.PurchaseEvaluations };
|
||||
foreach (var typeCode in MenuKeys.PurchaseEvaluationTypeCodes)
|
||||
{
|
||||
peKeys.Add(MenuKeys.PurchaseEvaluationGroup(typeCode));
|
||||
peKeys.Add(MenuKeys.PurchaseEvaluationWorkflowView(typeCode));
|
||||
peKeys.Add(MenuKeys.PurchaseEvaluationList(typeCode));
|
||||
peKeys.Add(MenuKeys.PurchaseEvaluationCreate(typeCode));
|
||||
peKeys.Add(MenuKeys.PurchaseEvaluationPending(typeCode));
|
||||
}
|
||||
|
||||
var reviewKeys = MenuKeys.All.Where(InReviewScope)
|
||||
.Concat(peKeys)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
var roles = await roleManager.Roles.ToListAsync();
|
||||
|
||||
// Load full rows (cần mutate CanRead/CanCreate cho Pe_* upgrade path).
|
||||
var existingRows = (await db.Permissions
|
||||
.Where(p => reviewKeys.Contains(p.MenuKey))
|
||||
.ToListAsync())
|
||||
.ToDictionary(p => (p.RoleId, p.MenuKey));
|
||||
|
||||
var added = 0;
|
||||
var upgraded = 0;
|
||||
foreach (var role in roles)
|
||||
{
|
||||
foreach (var key in reviewKeys)
|
||||
{
|
||||
var isPe = IsPeKey(key);
|
||||
if (existingRows.TryGetValue((role.Id, key), out var row))
|
||||
{
|
||||
// [S57bis] Pe_* upgrade-only: nâng CanRead/CanCreate nếu đang false.
|
||||
if (isPe)
|
||||
{
|
||||
var changed = false;
|
||||
if (!row.CanRead) { row.CanRead = true; changed = true; }
|
||||
if (!row.CanCreate) { row.CanCreate = true; changed = true; }
|
||||
if (changed) upgraded++;
|
||||
}
|
||||
// Key non-Pe: skip-existing (giữ nguyên như cũ).
|
||||
continue;
|
||||
}
|
||||
|
||||
db.Permissions.Add(new Permission
|
||||
{
|
||||
RoleId = role.Id,
|
||||
MenuKey = key,
|
||||
CanRead = true,
|
||||
CanCreate = isPe, // [S57bis] Pe_* được Tạo; còn lại read-only
|
||||
CanUpdate = false,
|
||||
CanDelete = false,
|
||||
});
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
if (added > 0 || upgraded > 0)
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
logger.LogInformation(
|
||||
"Seeded all-roles review perms: {Added} added + {Upgraded} upgraded (Pe_* read+create) " +
|
||||
"({Keys} keys × {Roles} roles)",
|
||||
added, upgraded, reviewKeys.Length, roles.Count);
|
||||
}
|
||||
}
|
||||
|
||||
// [Plan CA S29 2026-05-22] Permission defaults cho role CatalogManager.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddWorkItemToPurchaseEvaluation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "WorkItemId",
|
||||
table: "PurchaseEvaluations",
|
||||
type: "uniqueidentifier",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PurchaseEvaluations_WorkItemId",
|
||||
table: "PurchaseEvaluations",
|
||||
column: "WorkItemId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_PurchaseEvaluations_WorkItemId",
|
||||
table: "PurchaseEvaluations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WorkItemId",
|
||||
table: "PurchaseEvaluations");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4942,6 +4942,9 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.Property<Guid?>("UpdatedBy")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("WorkItemId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("WorkflowDefinitionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
@ -4961,6 +4964,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
|
||||
b.HasIndex("SlaDeadline");
|
||||
|
||||
b.HasIndex("WorkItemId");
|
||||
|
||||
b.HasIndex("WorkflowDefinitionId");
|
||||
|
||||
b.HasIndex("Phase", "IsDeleted");
|
||||
|
||||
Reference in New Issue
Block a user