[CLAUDE] Domain+App+Infra+Admin: W1 KHKK — Mig 69 ContractSigningPlans 7 bang + policy KeHoachKyKet + seeder UPGRADE-if-exists + CodeGen KHKK + Designer type-10
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m40s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m40s
- Domain/ContractSigningPlans: 7 entity + 3 enum (Phase 1/2/3/98/99; DossierItem Kind/Status start=1); ApplicableType += ContractSigningPlan=10 (slot 10, append-only) - Mig 69 AddContractSigningPlans: 7 CreateTable / 20 CreateIndex / 0 ALTER; UNIQUE filtered (PlanId,SupplierId) WHERE IsDeleted=0 (#57); FK 6 con Cascade 1-hop + 2 Restrict, 0 multiple-cascade-path; database-agent review PASS 8/8 + DB11 - MenuKeys.All += KeHoachKyKet (policy KeHoachKyKet.* x4 sinh runtime; menu-row prod KHONG doi — 142 giu); STATUS canonical 54->55 / 216->220 - DbInitializer.SeedKeHoachKyKetAccessAsync: UPGRADE-if-exists (F-S1 — khong insert-NO-OP) + vế CanDelete (Drafter/Admin), 13/13 role CanCreate+CanUpdate, chay SAU revoke; revoker khong phu Khkk_* (reviewer Truc-1 PASS) - ContractSigningPlanCodeGenerator: KHKK/{YYYY}/{Seq:D3} (owner OK @S161) qua WorkflowAppCodeSequences dung chung, SERIALIZABLE - AWV2AdminFeatures: 3 site count/retained/orphan + nhanh ContractSigningPlanLevelOpinions (chong no FK 500 khi xoa Level); +label VN type-10 (F-7); +ep false 2 co early-finalize server-side cho type-10 (F-8 — containment tang BE, FE da ep o levelFlagsPayload) - fe-admin Designer: deep-link type-10 + an 2 checkbox early-finalize (anh phan "PHA VO" S155) — containment 2 lop render+payload - tests: +4 (566 total, 0 fail) — schema-invariant 7 bang + codegen format (fault-inject 2 chieu chung co rang) Reviewer: PASS_WITH_FLAGS 10 issue -> 8 fixed cung commit, 2 giu trang thai (F-1 acceptance đo-CO cho prod; F-4 ke thua y PE prod) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -64,6 +64,10 @@ public enum ApprovalWorkflowApplicableType
|
||||
VehicleBooking = 7, // G-O5 — Đặt xe công
|
||||
ItTicket = 8, // G-O6 — Ticket CNTT
|
||||
TravelRequest = 9, // G-O4 — Đơn công tác (Travel) — Phase 11 P11-A
|
||||
|
||||
// [W1 Mig 69 — S161 2026-07-29] GĐ2 "Kế hoạch ký kết HĐ" (ContractSigningPlan).
|
||||
// Append-only: slot 10 re-verify TRỐNG ngay trước khi viết (enum trước dừng ở 9).
|
||||
ContractSigningPlan = 10, // GĐ2 — Kế hoạch ký kết HĐ (NCC/Thầu phụ)
|
||||
}
|
||||
|
||||
// Bước = Phòng. 1 quy trình có nhiều bước theo Order.
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161 2026-07-29] Aggregate root "Kế hoạch ký kết HĐ" (GĐ2, tờ FO-002.01).
|
||||
// Nối tiếp phiếu Duyệt NCC (PE) đã DaDuyet → chốt giá per-NCC trước khi sinh HĐ (GĐ3).
|
||||
//
|
||||
// Cookie-cutter mirror PurchaseEvaluation (con-trỏ ĐÔI StepIndex + LevelOrder cho
|
||||
// workflow V2 OR-of-N) — KHÔNG mirror Proposal (Proposal chỉ có LevelOrder, flatten
|
||||
// SelectMany làm OR-of-N thành AND, xem spec §2.1).
|
||||
//
|
||||
// Convention loose-Guid: PurchaseEvaluationId / ProjectId / DepartmentId / DrafterUserId
|
||||
// KHÔNG FK vật lý (mirror PE `PurchaseEvaluation.cs:15-18` + Mig 49). FK vật lý CHỈ:
|
||||
// nội bộ module (Lines/DossierItems/... Cascade) + ApprovalWorkflowId → bảng V2 dùng chung.
|
||||
public class ContractSigningPlan : AuditableEntity
|
||||
{
|
||||
public string? MaKeHoach { get; set; } // "KHKK/2026/001" — gen atomic lúc Submit (W2)
|
||||
|
||||
public Guid PurchaseEvaluationId { get; set; } // loose-Guid + IX — phiếu PE nguồn (DaDuyet)
|
||||
public Guid ProjectId { get; set; } // denorm để lọc list (loose-Guid, mirror PE)
|
||||
public Guid? DepartmentId { get; set; } // [C5 review-schema] phòng của phiếu — vế `DeptManager`
|
||||
// của guard trình so với cột NÀY (Step.DepartmentId chỉ là
|
||||
// hint của quy trình, KHÔNG thay được phòng của phiếu).
|
||||
public Guid DrafterUserId { get; set; } // BCH soạn kế hoạch
|
||||
|
||||
public ContractSigningPlanPhase Phase { get; set; } = ContractSigningPlanPhase.DangSoanThao;
|
||||
|
||||
// Workflow V2 pin (ApplicableType = ContractSigningPlan = 10). Con-trỏ ĐÔI mirror PE:
|
||||
// StepIndex 0-based (Bước) + LevelOrder 1-based (Cấp trong Bước). Null khi terminal.
|
||||
public Guid? ApprovalWorkflowId { get; set; }
|
||||
public int? CurrentWorkflowStepIndex { get; set; }
|
||||
public int? CurrentApprovalLevelOrder { get; set; }
|
||||
|
||||
// 🔸 SLA = FE hiển thị THAM KHẢO (O-2 owner chốt). KHÔNG đăng ký SlaExpiryJob,
|
||||
// KHÔNG có cờ SlaWarningSent — cố ý khai rõ để cột này không rơi vào lớp
|
||||
// "cột có mà 0 ai đọc"; người đọc nó là FE, không phải job.
|
||||
public DateTime? SlaDeadline { get; set; }
|
||||
|
||||
public string? HoSoLink { get; set; } // hyperlink thư mục hồ sơ NAS (mirror PE HoSoLink)
|
||||
public string? GhiChu { get; set; }
|
||||
|
||||
public List<ContractSigningPlanLine> Lines { get; set; } = new();
|
||||
public List<ContractSigningPlanDossierItem> DossierItems { get; set; } = new();
|
||||
public List<ContractSigningPlanAttachment> Attachments { get; set; } = new();
|
||||
public List<ContractSigningPlanChangelog> Changelogs { get; set; } = new();
|
||||
public List<ContractSigningPlanApproval> Approvals { get; set; } = new();
|
||||
public List<ContractSigningPlanLevelOpinion> LevelOpinions { get; set; } = new();
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
using SolutionErp.Domain.Contracts; // reuse ApprovalDecision enum
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161] Lịch sử transition (1 row / lần chuyển phase).
|
||||
// 🔴 THÊM sau review (lens-schema H1): service được lệnh copy CÓ ghi vào bảng này
|
||||
// (`ContractWorkflowService.cs:151` `db.ContractApprovals.Add(...)`), twin PE
|
||||
// `PurchaseEvaluation.cs:137` + index (PeId, ApprovedAt). Thiếu ⇒ đoạn copy trỏ
|
||||
// bảng KHÔNG TỒN TẠI.
|
||||
//
|
||||
// BaseEntity (append-only, KHÔNG soft-delete) — mirror PurchaseEvaluationApproval.
|
||||
//
|
||||
// `ApprovalWorkflowLevelId` = loose-Guid CỐ Ý KHÔNG FK vật lý: nếu để FK Restrict thì
|
||||
// admin xoá Level sẽ nổ 500 vì lịch sử duyệt, mà 3 site dọn dẹp trong
|
||||
// ApprovalWorkflowV2AdminFeatures chỉ biết bảng *LevelOpinions (không biết *Approvals).
|
||||
public class ContractSigningPlanApproval : BaseEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
|
||||
public ContractSigningPlanPhase FromPhase { get; set; }
|
||||
public ContractSigningPlanPhase ToPhase { get; set; }
|
||||
public ApprovalDecision Decision { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public Guid? ApprovalWorkflowLevelId { get; set; } // Cấp đã ký (loose-Guid, no FK)
|
||||
public Guid? ApprovedByUserId { get; set; } // null = system/SLA auto
|
||||
public DateTime ApprovedAt { get; set; }
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// 🔴 Bắt đầu từ 1 (0 falsy JS); `Other = 99` khuôn PurchaseEvaluationAttachmentPurpose.
|
||||
// Append-only khi cần thêm mục đích mới.
|
||||
public enum ContractSigningPlanAttachmentPurpose
|
||||
{
|
||||
DossierScan = 1, // Bản scan căn cứ (mẫu vật liệu / shopdrawing / kết quả TVGS)
|
||||
PlanDocument = 2, // Bản kế hoạch ký kết đã ký/xuất
|
||||
Other = 99,
|
||||
}
|
||||
|
||||
// [W1 Mig 69 — S161] File đính kèm phiếu Kế hoạch ký kết.
|
||||
// FK Cascade Plan (1 đường cascade DUY NHẤT). `ContractSigningPlanDossierItemId`
|
||||
// optional = loose-Guid + index, CỐ Ý KHÔNG FK vật lý — khuôn
|
||||
// `PurchaseEvaluationAttachment.PurchaseEvaluationSupplierId` (config chỉ HasIndex).
|
||||
// Lý do kỹ thuật: Plan→DossierItem→Attachment + Plan→Attachment cùng Cascade sẽ là
|
||||
// MULTIPLE CASCADE PATHS ⇒ SQL Server từ chối tạo bảng.
|
||||
// Khuôn ProposalAttachment.cs + PurchaseEvaluationAttachment.cs.
|
||||
public class ContractSigningPlanAttachment : AuditableEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
public Guid? ContractSigningPlanDossierItemId { get; set; }
|
||||
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
public string StoragePath { get; set; } = string.Empty;
|
||||
public long FileSize { get; set; }
|
||||
public string ContentType { get; set; } = string.Empty;
|
||||
public ContractSigningPlanAttachmentPurpose Purpose { get; set; } = ContractSigningPlanAttachmentPurpose.Other;
|
||||
public string? Note { get; set; }
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
using SolutionErp.Domain.Contracts; // reuse ChangelogAction enum
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161] Phạm vi thay đổi được ghi nhật ký. Bắt đầu từ 1 (0 falsy JS).
|
||||
public enum ContractSigningPlanEntityType
|
||||
{
|
||||
Header = 1,
|
||||
Line = 2,
|
||||
DossierItem = 3,
|
||||
Workflow = 4,
|
||||
Attachment = 5,
|
||||
}
|
||||
|
||||
// [W1 Mig 69 — S161] Nhật ký thay đổi phiếu Kế hoạch ký kết — CÓ TỪ NGÀY 1
|
||||
// (tiền lệ PE Mig 60/61: thiếu changelog thì không backfill lại được lịch sử).
|
||||
//
|
||||
// 🔴 4 cột EntityType / EntityId / UserName / FieldChangesJson là BẮT BUỘC (review
|
||||
// lens-schema C1): `LogTransitionAsync` khuôn `ContractWorkflowService.cs:405` gọi
|
||||
// `changelog.LogWorkflowTransitionAsync` cần đủ chữ ký này, thiếu ⇒ đoạn copy KHÔNG
|
||||
// BIÊN DỊCH + mất index twin (PlanId, EntityType).
|
||||
//
|
||||
// BaseEntity (KHÔNG soft-delete) — append-only audit, mirror PurchaseEvaluationChangelog.
|
||||
public class ContractSigningPlanChangelog : BaseEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
|
||||
public ContractSigningPlanEntityType EntityType { get; set; }
|
||||
public Guid? EntityId { get; set; }
|
||||
public ChangelogAction Action { get; set; }
|
||||
public ContractSigningPlanPhase? PhaseAtChange { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public string? FieldChangesJson { get; set; }
|
||||
public string? ContextNote { get; set; }
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161] Loại căn cứ (mục b.8-9 tờ FO-002.01).
|
||||
// 🔴 Bắt đầu từ 1 — 0 falsy trong JS. `Khac = 99` theo khuôn `ItTicketCategory.Other = 99`.
|
||||
public enum DossierItemKind
|
||||
{
|
||||
MauVatLieu = 1, // Mẫu vật liệu trình TVGS
|
||||
Shopdrawing = 2, // Bản vẽ shopdrawing
|
||||
Khac = 99,
|
||||
}
|
||||
|
||||
// [W1 Mig 69 — S161] Trạng thái 1 căn cứ.
|
||||
// 🔴 ChuaNop = 1 (KHÔNG 0): mọi enum repo bắt đầu ở 1 (`Office/Enums.cs:9,19,31,41,50,58`;
|
||||
// `ApprovalWorkflow.cs:55`) và 0 là falsy JS ⇒ `status ? A : B` / `?status=0` rơi im lặng ở FE.
|
||||
public enum DossierItemStatus
|
||||
{
|
||||
ChuaNop = 1,
|
||||
DaNop = 2,
|
||||
TvgsDuyet = 3,
|
||||
TvgsBac = 4,
|
||||
}
|
||||
|
||||
// [W1 Mig 69 — S161] Danh mục căn cứ kèm kế hoạch ký kết — INPUT của con số đang duyệt.
|
||||
// CRUD chỉ mở ở phase {DangSoanThao, TraLai} (W2).
|
||||
public class ContractSigningPlanDossierItem : AuditableEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
|
||||
public DossierItemKind Kind { get; set; } = DossierItemKind.Khac;
|
||||
public Guid? SupplierId { get; set; } // loose-Guid — căn cứ gắn NCC nào (null = chung)
|
||||
public string Name { get; set; } = string.Empty; // "Mẫu thép D10 Hoà Phát"
|
||||
public DossierItemStatus Status { get; set; } = DossierItemStatus.ChuaNop;
|
||||
|
||||
public string? TvgsName { get; set; } // tên TVGS xử lý
|
||||
public DateTime? TvgsResultAt { get; set; } // ngày TVGS duyệt/bác
|
||||
public string? Note { get; set; }
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using SolutionErp.Domain.ApprovalWorkflowsV2;
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161] Ý kiến cấp duyệt V2 cho Kế hoạch ký kết.
|
||||
// Cookie-cutter mirror ContractLevelOpinion (Mig 33) / PurchaseEvaluationLevelOpinion (Mig 26)
|
||||
// — twin thứ 8 của họ `*LevelOpinion`.
|
||||
//
|
||||
// Mỗi row = 1 (Plan × ApprovalWorkflowLevel). Service ApproveV2Async UPSERT sau khi
|
||||
// duyệt xong Cấp hiện tại (latest-write-wins). Reject (TraLai/TuChoi) KHÔNG sync.
|
||||
//
|
||||
// UNIQUE composite (PlanId, LevelId) — 1 row/level/phiếu.
|
||||
// FK Cascade Plan (xoá phiếu → wipe) + Restrict Level (admin xoá Level bị chặn khi
|
||||
// còn chữ ký — bảo vệ data; 3 site liệt-kê-thủ-công trong ApprovalWorkflowV2AdminFeatures
|
||||
// PHẢI biết bảng này, thiếu ⇒ admin xoá Level có chữ ký nổ FK 500).
|
||||
//
|
||||
// 🔴 CỐ Ý KHÔNG có HasQueryFilter (parity 7 twin còn lại): site "dọn chữ ký MỒ CÔI"
|
||||
// (`ApprovalWorkflowV2AdminFeatures.cs` ExecuteDeleteAsync) phải THẤY được row đã
|
||||
// soft-delete, nếu bị filter che thì row mồ côi sống sót và FK Restrict lại nổ 500.
|
||||
public class ContractSigningPlanLevelOpinion : AuditableEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
public Guid ApprovalWorkflowLevelId { get; set; }
|
||||
|
||||
public string? Comment { get; set; } // max 2000 hoặc placeholder
|
||||
public DateTime SignedAt { get; set; }
|
||||
public Guid SignedByUserId { get; set; } // người ký THẬT (có thể Admin duyệt thay)
|
||||
public string SignedByFullName { get; set; } = string.Empty; // snapshot denorm (KHÔNG nav sang User)
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
public ApprovalWorkflowLevel? Level { get; set; }
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161] Dòng giá per-NCC-trúng-thầu của Kế hoạch ký kết.
|
||||
// 1 row = 1 NCC (winner của PE nguồn). HĐ (GĐ3) sinh PER-NCC ⇒ dòng này là chỗ tự
|
||||
// nhiên để giữ đường quay ngược Kế-hoạch → HĐ (`ContractId`, twin `PurchaseEvaluation.ContractId`).
|
||||
//
|
||||
// AuditableEntity (soft delete) — BẮT BUỘC vì UNIQUE (PlanId, SupplierId) phải là
|
||||
// filtered `WHERE IsDeleted = 0` (gotcha #57: xoá mềm 1 NCC rồi thêm lại NCC đó
|
||||
// sẽ đụng unique nếu index không filter).
|
||||
public class ContractSigningPlanLine : AuditableEntity
|
||||
{
|
||||
public Guid ContractSigningPlanId { get; set; }
|
||||
public Guid SupplierId { get; set; } // loose-Guid (KHÔNG FK vật lý sang Suppliers)
|
||||
|
||||
// SNAPSHOT tổng báo giá của NCC này trên phiếu PE (SUM Quote.IsSelected) tại thời
|
||||
// điểm TẠO kế hoạch — chụp 1 lần, KHÔNG đọc live (PE sửa sau không được làm trôi
|
||||
// số của kế hoạch đã lập; cùng triết lý freeze ngân sách Mig 67).
|
||||
public decimal PeReferenceAmount { get; set; }
|
||||
|
||||
public decimal ProposedAmount { get; set; } // BCH đề xuất
|
||||
public decimal? ApprovedAmount { get; set; } // CHỈ ghi tại choke-point finalize (W3)
|
||||
|
||||
public Guid? ContractId { get; set; } // [C6 review-schema] HĐ sinh ra từ dòng này (W5)
|
||||
public string? Note { get; set; }
|
||||
|
||||
public ContractSigningPlan? ContractSigningPlan { get; set; }
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
namespace SolutionErp.Domain.ContractSigningPlans;
|
||||
|
||||
// [W1 Mig 69 — S161 2026-07-29] Trạng thái phiếu "Kế hoạch ký kết HĐ" (GĐ2).
|
||||
//
|
||||
// Giá trị SẠCH, KHÔNG copy 7/98 của PurchaseEvaluationPhase (sẹo lịch sử enum PE —
|
||||
// phiếu mới không nợ data cũ). Giữ 98/99 cho TraLai/TuChoi để quen mắt FE badge.
|
||||
// 🔴 Bắt đầu từ 1 — 0 là falsy trong JS (`status ? A : B`, `?phase=0` rơi im lặng ở FE).
|
||||
public enum ContractSigningPlanPhase
|
||||
{
|
||||
DangSoanThao = 1, // Nháp — Drafter (BCH) soạn
|
||||
ChoDuyet = 2, // Đã gửi duyệt — chạy workflow V2 (ApplicableType=10)
|
||||
DaDuyet = 3, // Terminal — chốt giá (ApprovedAmount)
|
||||
TraLai = 98, // Approver trả lại cho người soạn (phase RIÊNG, mirror PE)
|
||||
TuChoi = 99, // Terminal — từ chối
|
||||
}
|
||||
@ -35,9 +35,10 @@ public static class MenuKeys
|
||||
["ThauPhu", "GiaoKhoan", "NhaCungCap", "DichVu", "MuaBan", "NguyenTacNcc", "NguyenTacDv"];
|
||||
|
||||
// [S159 2026-07-29] 2 root PLACEHOLDER giai đoạn 2/4 (owner: menu hiển thị đúng
|
||||
// 4 giai đoạn như tab strip). CỐ Ý NGOÀI `All` — chưa có module ⇒ không sinh
|
||||
// policy per-action (giữ canonical Menu keys/Policies không drift); FE trỏ về
|
||||
// /dashboard. Wave-1 GĐ2 (spec KHKK) phải TÁI DÙNG/thay root `KeHoachKyKet` này.
|
||||
// 4 giai đoạn như tab strip). [S161 W1 — reviewer F-3] TÁCH TRẠNG THÁI 2 key:
|
||||
// - `KeHoachKyKet` ĐÃ VÀO `All` @S161 (W1 KHKK) ⇒ 4 policy KeHoachKyKet.* sinh runtime.
|
||||
// - `HopDongCung` VẪN NGOÀI `All` — chưa có module ⇒ không sinh policy (GĐ4 = 1-mốc
|
||||
// upload trên Contract, có thể không bao giờ cần policy riêng).
|
||||
public const string KeHoachKyKet = "KeHoachKyKet";
|
||||
public const string HopDongCung = "HopDongCung";
|
||||
|
||||
@ -176,6 +177,7 @@ public static class MenuKeys
|
||||
OffDonTu, OffDonTuLeave, OffDonTuOt, OffDonTuTravel, // Phase 10.3 G-O4 — Đơn từ
|
||||
OffDatXe, OffItTicket, OffChamCong, OffAttendanceReport, HrmDashboard, // Phase 10.3-10.4 — G-O5/G-O6/G-P1/G-H3 + P11-E report
|
||||
Personal, // [S57] Cá nhân (Puro grouping — Chấm công re-parent)
|
||||
KeHoachKyKet, // [W1 Mig 69 — S161] GĐ2 KHKK: const :41 vào All ⇒ sinh policy KeHoachKyKet.{Read,Create,Update,Delete}
|
||||
System, Users, Roles, Permissions, MenuVisibility, Workflows, PeWorkflows,
|
||||
ApprovalWorkflowsV2, ApprovalWorkflowDuyetNccV2, ApprovalWorkflowDuyetNccPhuongAnV2, // Mig 22
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user