7.7 KiB
SPEC WAVE-1 — Schema Mig 69 + Permission + Designer type-10 (GĐ2 KHKK)
Nguồn RATIFIED: spec cũ §2.2 (7 bảng) + §2.4 (menu/permission) + §③ WAVE-1 (
spec-ke-hoach-ky-ket-hd-28-07-2026.md:266-308) + NOTE W1 @S160 (đầu §WAVE-1). Plan cha default #6 #12. File này = CÁCH-LÀM cụ thể + checklist; xung đột nào với spec cũ đã khai ở mục Δ.
① TÍNH NĂNG / ĐỀ BÀI
Đặt nền DB + quyền + cho admin dựng được quy trình duyệt type=10, để W2 tạo phiếu được ngay. Deploy riêng an toàn: menu skeleton Khkk_* đã public từ arc-2 (leaf ComingSoon — dark-launch nằm ở TRANG, không ở menu). Mig 69 = migration DUY NHẤT của toàn plan dry-run.
Δ so spec cũ (2 điểm, do thực địa arc-2 đổi tiền đề):
- KHÔNG thêm 4 const
ContragSigningPlans*/menu 4 dòng như §2.4 — menu skeletonKhkk_G1+6 leaf ĐÃ SEED prod (CanRead 13/13). Thay bằng:All += KeHoachKyKet(const sẵnMenuKeys.cs:41) → policyKeHoachKyKet.{Read,Create,Update,Delete}tự sinh (Program.csforeach All×Actions) + seeder grant 4-action cho role đích. Menu-key hiển thị (Khkk_*) ⟂ policy-key (KeHoachKyKet) — 2 lớp khác nhau, khai rõ để reviewer soi. - Label VN = "Kế hoạch ký kết HĐ" (đời nhãn cuối @S159), KHÔNG phải "Đề xuất ký kết hợp đồng" còn trong spec cũ
:223.
② CÁCH IMPLEMENT (thứ tự bắt buộc)
- Gate 5-anchor (§③-A) — trượt ≥1 ⇒ chấm lại hết rồi mới code.
- Domain (
src/Backend/SolutionErp.Domain/ContractSigningPlans/): 7 entity đúng bảng spec cũ §2.2 (#1 headerContractSigningPlans[+DepartmentId Guid?C5] · #2Lines[+ContractId Guid?C6, UNIQUE filtered(PlanId,SupplierId) WHERE IsDeleted=0] · #3DossierItems· #4LevelOpinions[UNIQUE (PlanId,LevelId), FK Cascade-Plan/Restrict-Level] · #5Attachments· #6Changelogs[ĐỦ 4 cột EntityType/EntityId/UserName/FieldChangesJson] · #7Approvals). 2 enum:ContractSigningPlanPhase{DangSoanThao=1,ChoDuyet=2,DaDuyet=3,TraLai=98,TuChoi=99}·DossierItemStatus{ChuaNop=1,DaNop=2,TvgsDuyet=3,TvgsBac=4}(🔴 bắt đầu 1, KHÔNG 0 — falsy JS) +DossierItemKind{MauVatLieu=1,Shopdrawing=2,Khac=99}. ApprovalWorkflowApplicableType+=ContractSigningPlan = 10— re-verify slot 10 trống NGAY TRƯỚC khi viết (ApprovalWorkflow.cs:53-67dừng ở 9; anchor #2).- EF Configuration ×7 — khai độ dài MỌI cột chuỗi (spec cũ
:134-137: MaKeHoach 50 · GhiChu/ContextNote 2000 · Name 500 · Note 1000 · TvgsName 200 · Summary 1000 · SignedByFullName 200 IsRequired · FileName 500/Path 1000/ContentType 200) +HasQueryFilter(!IsDeleted)header + loose-Guid (KHÔNG FK vật lý sang PE/Supplier — convention Mig 49). - DbSets vào
IApplicationDbContext+ApplicationDbContext. - Mig 69
AddContractSigningPlans(3-file rule: migration + Designer + Snapshot; skillef-core-migration): 7 CreateTable, 0 ALTER bảng cũ, Down = DropTable ×7.database-agentdesign-review TRƯỚC apply (per spec cũ W1 vai). - MenuKeys.cs:
All+=KeHoachKyKet(chỉ 1 dòng — const đã có :41). KHÔNG key mới. - 🔴 DbInitializer grant — UPGRADE-IF-EXISTS, KHÔNG INSERT (review F-S1/F-O3/F-O4 — 4 lane hội tụ, lead verify đĩa CONFIRMED): row Permission key
KeHoachKyKet+Khkk_*ĐÃ tồn tại CanRead-only cho mọi role (DbInitializer.cs:2204-2213nhánh read-only), và nhánh grant key-non-Pe là skip-existingcontinue(:2242-2245) ⇒ thêm grant kiểu insert = NO-OP im lặng ⇒ CanCreate/Update giữfalse⇒ W2/W3 lên prod 403 cho 13/13 role, chỉ Admin qua = GIẾT dry-run (gotcha #44). Fix: viết method mới khuônSeedProcurementMasterAccessAsync:2367-2374(if (existing.TryGetValue(key,out row)) { if(!row.CanCreate){row.CanCreate=true;…} }), chạy SAURevokeTemporarilyHidden…(mirror:2098-2101) để thắng. Cấp CanCreate/CanUpdate cho 13/13 role (đúng O-A "hiển thị hết để góp ý") + CanDelete cho Drafter/Admin, trênKeHoachKyKet+ 6 leafKhkk_*. 🔴 CẤM SQL tay (gotcha #84 — vếtDbInitializer.cs:2287-2296Run #423 grant tay bị revoker lật 447/494). (b) KHÔNG đụng menu-rowKhkk_*(đã seed). (c) label chỉnh = labelBackfill (gotcha #11). - Mã phiếu: CodeGen service prefix
"KHKK/{YYYY}"quaWorkflowAppCodeSequence(spec cũ #8-CẮT: bảng dùng chung 4 module Office, atomic = transaction SERIALIZABLE trong service,ContractCodeSequence.cs:5). - fe-admin Designer type-10:
ApprovalWorkflowsV2Page.tsxmapTYPE_CODE_TO_INT(site thực:144-148, KHÔNG phải comment:143— review chéo) + 3 siteApprovalWorkflowV2AdminFeatures.csliệt-kê-thủ-công (:911/:929merge chữ-ký-sống ·:971/:992RetainedOpinionRef ·:1044/:1056chữ ký mồ côi) — thiếu ⇒ admin xoá Level có chữ ký nổ FK 500. 🔴 Ẩn 2 checkbox early-finalize cho type=10 (review F-B3 — containment THẬT): Designer V2 render checkboxAllowApproverFinalize/AllowApproverSkipToFinal(ApprovalWorkflowsV2Page.tsx:1267-1268) = per-Level admin opt-in. Anh phán 2 đường này "PHÁ VỠ" (S155). W3 KHÔNG port code path ⇒ để checkbox sống mà backend không xử = cờ chết đánh lừa admin. Fix: ẩn/disable 2 checkbox khitypeCode==10(hoặc render read-only + tooltip "chưa hỗ trợ cho quy trình KHKK"). Đây là cách làm default #1 ("đường THƯỜNG") thành containment thật thay vì "port-nhưng-không-kích-hoạt" (giả). - Build + test + commit
[CLAUDE] Domain+Infra+Admin: W1 KHKK schema Mig 69 + policy KeHoachKyKet + Designer type-10.
③ CHECKLIST
A. Gate 5-anchor (mở TRƯỚC): ApprovalWorkflow.cs:53-67 (slot 10 trống) · MenuKeys.cs:41 (const KeHoachKyKet + comment tái dùng) · MenuKeys.cs:163+ (All chưa có KeHoachKyKet) · AuthorizePolicyRegressionTests.cs:138-139 (lưới policy-ngoài-All ⇒ 500) · DbInitializer.cs:1779-1786 (Khkk_* 7 key đã seed).
B. Acceptance (từ spec cũ W1 :281-300, giữ nguyên hiệu lực + bổ sung):
dotnet ef migrations list→ migration cuối chứaAddContractSigningPlans(KHÔNG có tiền tố69_).sqlcmdlocal:SELECT COUNT(*) FROM sys.tables WHERE is_ms_shipped=0→ 89 → 96.dotnet test→AuthorizePolicyRegressionTestsXANH (policyKeHoachKyKet.*hợp lệ — All đã chứa key).- Test mới tồn tại ĐÚNG TÊN:
ContractSigningPlanSchemaTests.BayBang_MoiBang_CoAuditFields_VaQueryFilter+ sànN ≥ 2test mới, PASS 0 fail (số-sau ≥ số-đo-ngay-trước + 2). - Admin tạo được quy trình
type=103 Bước (PMH→CCM→CEO), pinIsUserSelectable; xoá 1 Level chưa chữ ký → OK. dotnet ef migrations script+ Down chạy sạch trên DB copy.- 🔴 Policy-grant ĐO CỜ, KHÔNG đếm ROW (review F-04: row đã có sẵn ⇒ đếm row = 0-bit): sau seed,
sqlcmd/curl xác nhận Permission keyKeHoachKyKetcó CanCreate=1 ∧ CanUpdate=1 cho ≥1 role non-Admin (vd Drafter). Row đã tồn tại từ trước ⇒ chỉ CỜ mới chứng upgrade ăn. - cicd 3-chân-kiềng sau deploy: CI PASS ·
__EFMigrationsHistorytop = Mig 69 (curl API không đo được mig ⇒ dùng sqlcmd qua RDP-người hoặc chấp nhận CI-log làm chứng) · sys.tables 96 · menu KHÔNG đổi (142 giữ — W1 không thêm menu). C. Rủi ro tick trước khi đóng wave: [ ] không quên HasQueryFilter · [ ] UNIQUE filtered có WHERE IsDeleted=0 (gotcha #57) · [ ] 0 SQL tay (gotcha #84).