Files
solution-erp/.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w6-duong-ong-hd-v2-29-07-2026.md
2026-07-29 18:43:24 +07:00

5.3 KiB
Raw Blame History

SPEC WAVE-6 — Đường ống HĐ V2: view + inbox + vai-trình (GĐ3 sống)

Nguồn: lane-2 §1 (bảng SẴN/GAP/PARTIAL re-verify 07-29) + §6-W6. Điều kiện tiên quyết VẬN HÀNH của GĐ3 — không có nó, HĐ pin V2 xong vẫn "vô hình" với approver ("cơ chế đúng, đường đi không có"). Build độc lập W1-W3 (0 đụng file KHKK) — chạy sớm được.

① TÍNH NĂNG / ĐỀ BÀI

3 lỗ đường ống làm HĐ V2 không vận hành được (đo 07-29):

  1. Approver V2 KHÔNG XEM được HĐ ChoDuyet — view-guard GetEligiblePhases (ContractFeatures.cs:334-351) 0 entry ChoDuyet cho approver (:488-489 ném Forbidden).
  2. HĐ ChoDuyet KHÔNG vào inboxPhaseActorRoles (:421-430) 100% legacy phase.
  3. PMH role Procurement bị 403 khi trình — guard trình đòi DrafterDeptManager (ContractWorkflowService.cs:73-78). Wave này = 0 UI mới, 0 mig; sau deploy: approver thấy + mở + duyệt được HĐ đang chờ mình.

② CÁCH IMPLEMENT

  1. Gate 5-anchor (§③-A).
  2. Test-before 3 test (§③-B) RED.
  3. 🔴 View — ĐÚNG CÁCH per-HĐ, KHÔNG role-blanket (review F-S3/F-C2/F-01): GetEligiblePhases(userRoles) (ContractFeatures.cs:334-350) chữ ký chỉ nhận role ⇒ nếu chỉ nhét ChoDuyet/DaPhatHanh vào tập role thì mở toang cho MỌI user mang role đó (kể cả HĐ xóa mềm ở :381). Fix: thêm nhánh V2 membership-theo-HĐ ở CẢ 3 call-site của GetEligiblePhases (:301 List · :381 ListDeleted · :486 Detail — review F-S3 chỉ ra gate cũ thiếu 2 site) — user thấy HĐ ChoDuyet nếu là ApproverUserId của Level trong workflow pin CỦA CHÍNH HĐ đó, và HĐ DaPhatHanh nếu thuộc workflow HĐ đó HOẶC mang vai bản-cứng (Director/AuthorizedSigner/HrAdmin/CostControl — actor b.19/20/21, cho GĐ4/W7). Admin giữ thấy-hết. KHÔNG mở toang: user ngoài cả hai điều kiện → 403 y cũ. (Đây là điều kiện W7 phụ thuộc — F-C2.)
  4. Inbox: mirror khuôn PE ResolveV2InboxIdsAsync (precompute id-set các HĐ mà con-trỏ Step/Level hiện tại chứa ApproverUserId==actor) — GHÉP vào inbox query hiện có thay vì chỉ PhaseActorRoles legacy. Giữ nhánh legacy cho HĐ V1 (nếu prod xuất hiện lại data V1). 🔴 Nhánh ADMIN (F-B13, DR-4 chốt @S161): admin hiện lấy eligiblePhases = PhaseActorRoles.Keys (ContractFeatures.cs:440-441) mà dict :421-430 KHÔNG có ChoDuyet ⇒ ghép v2InboxIds xong admin VẪN mù HĐ ChoDuyet. Mirror TRỌN khuôn PE PurchaseEvaluationFeatures.cs:731-748: admin dùng danh-sách phase liệt TƯỜNG MINH + ChoDuyet (admin thấy mọi HĐ chờ duyệt qua nhánh PHASE; v2InboxIds để rỗng cho admin đúng như PE :746-748).
  5. Guard trình (ContractWorkflowService.cs:73-78): nới thành Drafter DeptManager CreatedBy==actor Procurement (dòng tối thiểu đóng lớp 403-PMH; giữ 403 cho role ngoài).
  6. (Nếu DTO cần) thêm currentApproval info vào detail DTO cho FE panel — chỉ khi FE thiếu dữ liệu hiển thị.
  7. Build + test GREEN + commit [CLAUDE] Contract: W6 duong ong V2 view+inbox+trinh. cicd verify.

③ CHECKLIST

A. Gate 5-anchor: ContractFeatures.cs:334-351 · :421-430 · :488-489 · ContractWorkflowService.cs:73-78 · ContractsController.cs:38-40 (khu inbox/list endpoint). B. Acceptance (lane-2 §7-W6):

  • 5 test ĐÚNG TÊN (khuôn repo) trong ContractV2PipelineTests: Inbox_PendingLevelApprover_SeesChoDuyetContract · Inbox_OtherLevel_DoesNotSeeContract (ca ÂM) · View_V2Approver_NotDrafter_CanSeeChoDuyetContract · List_HardCopyActor_NotAdmin_SeesDaPhatHanhContract (🔴 review F-01: LIST test cho actor bản-cứng — không wave nào đo LIST trước) · View_UserOutsideWorkflow_WithUnrelatedRole_Gets403 (🔴 review F-S4: ca ÂM pin ROLE cụ thể như CostControl/Procurement, KHÔNG chọn HrAdmin cho pass dễ).
  • dotnet test PASS 0 fail, số-sau ≥ baseline + 5.
  • Prod ③: login approver-V2 KHÔNG-admin → GET /api/contracts/inbox ≥1 HĐ ChoDuyet mà mình là Level hiện tại + GET /{id} 200; login admin/api/contracts/inbox ≥1 HĐ ChoDuyet (🔴 F-B13 — trước W6: admin 0 dòng vì PhaseActorRoles.Keys không có ChoDuyet); login actor bản-cứng non-admin (vd Director) → GET /api/contracts?type=1&phase=9 ≥1 dòng (trước W6: 0 dòng + 403).
  • Ca ÂM giữ (pin role): user role KHÔNG liên quan + không thuộc workflow HĐ nào → GET /{id} vẫn 403 (chứng KHÔNG mở toang qua role-blanket).
  • 🔴 Kiểm hở xóa-mềm: HĐ ChoDuyet đã soft-delete → user V2 KHÔNG thấy ở :381 ListDeleted (fix membership không được rò sang màn Đã-xóa).
  • CI run PASS; 0 mig (diff *Migrations* = 0 — khuôn M2 đợt-5). C. Rủi ro: [ ] đổi view-guard làm hở HĐ cho MỌI user (thiếu điều kiện per-HĐ) — ca ÂM pin-role + kiểm-xóa-mềm canh · [ ] inbox V2 quên lọc IsDeleted · [ ] SỬA CHỈ 1/3 call-site GetEligiblePhases (F-S3: gate cũ thiếu :301+:381) — grep 3 site trước khi đóng · [ ] đụng nhánh Reject-trước-guard (:49-66) = O-A anh chốt ĐỂ MỞ, ĐỪNG "tiện tay sửa".