Files
solution-erp/.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w5-cau-khkk-hd-29-07-2026.md
2026-07-29 19:03:31 +07:00

4.6 KiB
Raw Blame History

SPEC WAVE-5 — Cầu KHKK→Hợp đồng + pin V2 (GĐ2→GĐ3, sửa bridge cũ 1 file)

Nguồn: spec cũ §2.5 + §③ WAVE-5 (:397-403) + lane-2 §2 (chi tiết nhất). Anh ĐÃ MỞ gate chạm HĐ @S160 (run.md ④). Defaults plan cha #2 (giá KH thắng) + #3 (Q11 cảnh báo mềm). Chạy SAU W3 + W6.

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

Từ phiếu KHKK DaDuyet, tạo HĐ per-winner với giá = ApprovedAmount của đúng NCC (thay SUM báo giá PE), HĐ pin workflow V2 để trình được ngay (hết kẹt ConflictException). SỬA BRIDGE CŨ — KHÔNG đẻ endpoint mới (lane-2 §2c: 2 endpoint cùng nghĩa = lớp lỗi 2-site S155).

② CÁCH IMPLEMENT

  1. Gate 5-anchor (§③-A).
  2. CreateContractFromEvaluationFeatures.cs (file DUY NHẤT phía BE logic): (a) đọc plan DaDuyet của PE (Plans.Where(PurchaseEvaluationId==peId && Phase==DaDuyet)) — CÓ: giaTri = line.ApprovedAmount khớp w.SupplierId thay SUM :88-90, ghi ContextNote lệch "giá KH x vs SUM-PE y" vào changelog (khuôn :121-140, bài D4 S134); KHÔNG có: giữ SUM + cảnh báo mềm trong response (Q11 default). (b) +param ApprovalWorkflowId? vào command + body record PurchaseEvaluationsController.cs:379-382 (🔴 record-count controller == command — bài 10-point checklist) → pin V2 cho HĐ tạo ra; không truyền → fallback hiện trạng V1 + warning. 🔴 GIỮ guard ApplicableType==Contract (review F-B6): cửa-1 chống forge đã có ContractFeatures.cs:84-86 + comment :77-78 (chặn attacker forge PE-workflow-id type 1/2); pin ở bridge phải qua CÙNG guard đó — CẤM pin thẳng workflow type 1/2/10 vào HĐ. Test canh CreateContractCommandApplicableTypeTests (đã có) + ca ÂM mới. (c) 🆕 ghi ngược Line.ContractId = contract.Id per-winner trong vòng lặp :82-115 (cột C6 sẵn từ W1). (d) GIỮ nguyên: guard :53-60 · idempotency pe.ContractId :145 · atomic codegen :78-82 · mapping :101-107.
  3. FE ×2 app: màn KHKK Detail (phiếu DaDuyet) thêm nút "Tạo Hợp đồng" deep-link gọi CÙNG endpoint POST /api/purchase-evaluations/{peId}/create-contract (+field chọn workflow V2 cho HĐ). Màn PE giữ nút cũ (2 đường song song — Q11 default).
  4. Test-before 4 test (§③-B) + build ×2 + commit. ⚠️ Đây là commit CHẠM module Contract đầu tiên — cicd verify kỹ (endpoint 401-control-404, khuôn đợt-5).

③ CHECKLIST

A. Gate 5-anchor: CreateContractFromEvaluationFeatures.cs:53-60 (guard) · :68-71 (pin V1-only) · :88-90 (SUM) · :145 (idempotency) · ContractWorkflowService.cs:115-116 (ConflictException kẹt V2-less). B. Acceptance (lane-2 §7-W7 + spec cũ W5):

  • 5 test tồn tại ĐÚNG TÊN (khuôn repo English-predicate) trong ContractSigningPlanBridgeTests: CreateContract_WithApprovedPlan_SetsGiaTri_ToApprovedAmountPerSupplier · CreateContract_NoPlan_KeepsSumOfPeQuotes_AndWarnsSoft · CreateContract_PinsV2Workflow_ContractReachesLevelOneOnSubmit · CreateContract_WritesBackLineContractId_PerWinner · CreateContract_ForgePeWorkflowId_ThrowsApplicableTypeGuard (ca ÂM forge — F-B6).
  • dotnet test PASS 0 fail, số-sau ≥ baseline + 5; CreateContractFromEvaluationMultiWinnerTests + CreateContractCommandApplicableTypeTests (ĐÃ TỒN TẠI) VẪN XANH — chống hồi quy multi-winner + forge.
  • TÍNH-CHẤT: PE có W winner + plan DaDuyet → POST create-contract trả đúng W contractId; MỖI HĐ giaTri == ApprovedAmount của Line cùng supplierId; tổng W HĐ == SUM(ApprovedAmount) toàn plan.
  • 🔴 Prod curl (ZZTEST) — CHẠY TỚI LƯỢT DUYỆT, KHÔNG dừng ở trình (review F-B5: nhánh trình ContractWorkflowService.cs:70-89 KHÔNG đọc workflow ⇒ "204 KHÔNG 409" PASS SẴN, 0-bit): HĐ tạo với ApprovalWorkflowId V2 → GET /{id}currentApprovalLevelOrder=1 (vế có răng — chứng pin V2 ăn); → Cấp-1 approver POST /{id}/transitions duyệt → tiến Bước 2. HĐ tạo KHÔNG workflow → trình vẫn 204 (đường cũ) nhưng currentApprovalLevelOrder=null.
  • Changelog có ContextNote chứa CẢ 2 số khi giá KH ≠ SUM-PE.
  • FE build ×2 · cicd 3-chân-kiềng. C. Rủi ro: [ ] KHÔNG sửa lệch phép SUM cũ cho PE-không-plan (đường cũ phải y nguyên — test #2 canh) · [ ] body record lệch count controller↔command (silent 400) · [ ] 🔴 dry-run CẤM tạo HĐ HopDongMuaBan — không mã "MB" nào được sinh (anh bỏ MB @S161, plan cha default #7; codegen giữ nguyên chờ format thay).