Files
solution-erp/.claude/workflows/runs/2026-07-29-S161-khkk-w6-duongong/sub-implementer-backend-0.md
2026-07-29 23:00:12 +07:00

3.7 KiB

sub-implementer-backend-0 — W6 Đường ống HĐ V2 (view + inbox + vai-trình)

Run: 2026-07-29-S161-khkk-w6-duongong · Spec: .claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w6-duong-ong-hd-v2-29-07-2026.md Luật lượt này: BE-only · 0 mig · KHÔNG commit/push/slnx · KHÔNG viết test (đợt-2) · KHÔNG đụng fe-*/ · KHÔNG đụng Reject :49-66 (O-A) · KHÔNG đụng ContractSigningPlan* (KHKK W1-W3 đã ship). Ghi TỪNG MỤC trong lúc làm (chống #53).


§0 — ĐO TRƯỚC KHI SỬA (gate 5-anchor, đọc tươi 2026-07-29)

Anchor (spec §③-A) Đo thật Khớp? Nội dung
ContractFeatures.cs:334-351 :334 internal static List<ContractPhase> GetEligiblePhases(IReadOnlyList<string> userRoles):351 return phases.ToList(); chữ ký CHỈ nhận role ⇒ không biết HĐ nào
ContractFeatures.cs:421-430 :421 private static readonly Dictionary<ContractPhase, string[]> PhaseActorRoles = new():430 [DangDongDau] = [HrAdmin], 7 key, 0 key ChoDuyet
ContractFeatures.cs:488-489 :488 if (!isDrafter && !isEligibleByRole) :489 throw new ForbiddenException("Bạn không có quyền xem HĐ này.") 403 detail
ContractWorkflowService.cs:73-78 :73 if (!isAdmin && !isSystem :74 && !actorRoles.Contains(AppRoles.Drafter) :75 && !actorRoles.Contains(AppRoles.DeptManager)) :77-78 throw Forbidden "không đủ quyền trình duyệt HĐ." guard trình
ContractsController.cs:38-40 :38 [HttpGet("inbox")] :39 Inbox(CancellationToken ct) :40 => Ok(await mediator.Send(new GetMyInboxQuery(), ct)); endpoint inbox

Gate 5-anchor: PASS 5/5 content-exact.

3 call-site GetEligiblePhases (grep src/Backend, đủ 3 — risk C "sửa chỉ 1/3"):

  • ContractFeatures.cs:301ListContractsQueryHandler (List)
  • ContractFeatures.cs:381ListDeletedContractsQueryHandler (ListDeleted, IgnoreQueryFilters().Where(IsDeleted))
  • ContractFeatures.cs:486GetContractQueryHandler (Detail)
  • (:441-442PhaseActorRoles của inbox — call-site THỨ 4 của nhánh phase, không phải của GetEligiblePhases)

Khuôn nguồn để mirror (đọc tươi):

  • PE detail V2-approver: PurchaseEvaluationFeatures.cs:886-898 (AnyAsync(l => l.Step!.ApprovalWorkflowId == awId && l.ApproverUserId == uid) + isDraftPhase S89)
  • PE inbox admin phase liệt tường minh: PurchaseEvaluationFeatures.cs:731-742 (+ ChoDuyet :740)
  • PE v2InboxIds rỗng cho admin: PurchaseEvaluationFeatures.cs:746-748
  • PE ResolveV2InboxIdsAsync in-memory join: PurchaseEvaluationFeatures.cs:817-852
  • KHKK (chính tao viết W2): ContractSigningPlanFeatures.cs:229-264 (Resolve) + :275-283 (ResolveUserApprovalWorkflowIdsAsync)

Nền tảng đã verify TRƯỚC khi code (chống bẫy "quên lọc IsDeleted" — risk C):

  • ContractConfiguration.cs:44 b.HasQueryFilter(x => !x.IsDeleted)db.Contracts mặc-định ĐÃ loại HĐ xóa mềm ⇒ inbox + list + ResolveV2InboxIdsAsync tự an-toàn, KHÔNG cần .Where(!IsDeleted) tay (khác HRM).
  • Contract.cs:23/40/46 có đủ ApprovalWorkflowId / CurrentWorkflowStepIndex / CurrentApprovalLevelOrder; :16 DrafterUserId.
  • BaseEntity.CreatedBy kiểu Guid? (không phải string) ⇒ so trực tiếp với actorUserId.
  • grep tests/ cho ContractV2Pipeline|GetMyInboxQuery|ListContractsQuery|GetContractQuery = 0 hit ⇒ 3 handler này hiện KHÔNG có test cũ nào bám ⇒ sửa không thể phá test cũ qua đường assert; rủi ro duy nhất là compile + suite tổng.