Files
solution-erp/.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w7-ban-cung-hdc-29-07-2026.md
2026-07-29 16:54:56 +07:00

6.0 KiB
Raw Blame History

SPEC WAVE-7 — Bản cứng Hdc_* 7 leaf trang thật (GĐ4, b.19→21, upload-only)

Nguồn: lane-2 §3 (option A derive 0-mig — grounding đã SỬA: Hdc = 7 leaf per-ContractType, KHÔNG phải 6-leaf; KHÔNG có phase "KyCung") + lane-1 F-13/F-14/F-15 (nghiệp vụ ký/dấu/lưu/scan; ⚠️ bảng 6-leaf của F-15 xây trên tiền đề menu SAI — NỘI DUNG cột/field giữ, HÌNH menu theo lane-2) + F-11 (bản cứng sinh ở b.17-18 GĐ3 → GĐ4 hiển thị readonly phần đó). Build song song từ đầu; VERIFY cần HĐ DaPhatHanh (sau W5+W6).

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

7 leaf Hdc_<Code> ("01. HĐ Thầu phụ - Bảng cứng" … "07.") hết ComingSoon → trang theo dõi bản cứng per loại HĐ: list HĐ phase=9 DaPhatHanh + trạng thái bản cứng + thao tác 3 trạm b.19/20/21 dạng upload-only (ký GĐ → đóng dấu HRA/ISO → phát hành/lưu/scan). Option A: 0-migration — trạng thái DERIVE từ attachment; GĐ4 nằm SAU terminal nên KHÔNG thêm phase, KHÔNG hồi sinh DangDongDau=8 [LEGACY].

Mapping trạm → bằng chứng upload (đảo được sang option B nếu anh muốn field tường minh):

Trạm (QT) Ai Bằng chứng hệ thống Derive badge
b.19 Ký GĐ/NĐUQ BOD upload ScannedSigned (scan bản có chữ ký GĐ) "Đã ký GĐ" = has(ScannedSigned)
b.20 Đóng dấu HRA/ISO (gộp vào b.21 — option A không tách được 2 mốc; khai lệch so 4-mốc lane-1 F-14)
b.21 Phát hành + Lưu PB/BCH + CCM upload SealedCopy (scan bộ đóng dấu) + note bắt buộc: số bộ gốc · đã giao CCM/NTP · nơi lưu/Filing path (plan cha default #9) + upload scan cover FO-002.01 (luật (c) lane-1) "Đã lưu bản cứng" = has(SealedCopy)
Readonly từ GĐ3 (F-11): khối "Ký nháy & cover b.17-18" hiển thị từ attachments (InitialSigned mới) + LevelOpinions HĐ.

② CÁCH IMPLEMENT

  1. Gate 5-anchor (§③-A).
  2. BE nhỏ: (a) enum-extend AttachmentPurpose += InitialSigned = 4 (ký nháy b.17-18) + CoverChecklist = 5 (scan cover FO-002.01, luật (c) — review F-09: thiếu purpose cho cover) — int column, 0-mig (tiền lệ PE ComparisonTable=4; ContractAttachment.cs:5-11); (b) 🔴 thêm param type vào ListContractsQuery + controller (review F-C1/F-05 — GET /api/contracts HIỆN 0 CÓ type, ContractsController.cs:16-24 chỉ phase/supplierId/projectId): thêm [FromQuery] ContractType? type + filter handler ContractFeatures.cs:305-307 — KHÔNG lọc client-side pageSize:100 (trần 100/loại). (c) ContractListItemDto +2 cờ derive hasSealedCopy/hasInitialSigned trong projection (DTO hiện 12 field :323-327 KHÔNG trả attachments; 🔴 badge "Đã ký GĐ" derive từ InitialSigned/SealedCopy, KHÔNG từ ScannedSigned — review F-C6: ScannedSigned=2 nghĩa "scan có chữ ký NCC ở DangInKy", proxy-predicate sai vai b.19 GĐ, gotcha #71).
  3. FE ×2 app: page HardCopiesPage (khuôn MyContractsPage.tsx — ĐÃ đọc đủ type/phase params :29,:34-35): route /hard-copies?type=N → list GET /api/contracts?type=N&phase=9 + cột mã HĐ/NCC/dự án/giá trị + 2 badge derive + panel upload (3 purpose) + note theo default #9 + khối readonly GĐ3. 4-place mirror ×2 app: page + App.tsx route + Layout.tsx staticMap re-point 7 dòng Hdc_ (fe-user Layout.tsx:63-69, mirror fe-admin) khỏi /coming-soon?stage=4&t=NN + types. Empty-state khi loại chưa có HĐ phát hành.
  4. Authz: 🔴 anh chốt O-A (để mở) ⇒ Contract giữ [Authorize] trần, KHÔNG gắn policy mới cho trang Hdc/upload — mọi user auth thao tác được (đúng "hiển thị hết để góp ý"; rủi ro attachment-IDOR disclose plan §4.7). (Nếu sau này anh bật O-B thì mới dùng Contracts.{Read,Update} — Contracts ∈ All :168, policy sống sẵn.)
  5. Build ×2 + test + commit. cicd: byte-marker + menu KHÔNG đổi (142 giữ — chỉ re-point staticMap FE, seeder không đụng).

③ CHECKLIST

A. Gate 5-anchor: ContractAttachment.cs:5-11 · DbInitializer.cs:1862-1867 (7-leaf loop — chỉ ĐỌC, không sửa) · fe-user/src/components/Layout.tsx:63-69 · fe-user/src/pages/contracts/MyContractsPage.tsx:29-46 · ContractsController.cs:16-24 (list params type/phase). B. Acceptance (lane-2 §7-W8 đổi số wave):

  • Test ĐÚNG TÊN (khuôn repo) trong ContractHardCopyTests: ListHardCopies_ReturnsOnlyContracts_InDaPhatHanhPhase · ListHardCopies_FiltersByContractType (🔴 review F-C1 — chứng param type LỌC thật, không hiện trùng 7 leaf) · HardCopyBadge_DerivesFromSealedCopyAttachment.
  • dotnet test PASS 0 fail, số-sau ≥ baseline + 3.
  • FE build ×2 PASS; grep "Hdc_" fe-user/src/components/Layout.tsx | grep -c "coming-soon" = 0 (cùng lệnh fe-admin).
  • 🔴 Prod ③ — 2 loại KHÁC NHAU ra 2 danh sách KHÁC NHAU (chứng type lọc, review F-C1): GET /api/contracts?type=1&phase=9?type=2&phase=9 (khi có ≥1 HĐ mỗi loại); mọi item phase==9. Byte-marker bundle #77.
  • 🔴 Prod ③ — VAI THẬT thấy được (review F-C2/F-01, phụ thuộc W6): login actor bản-cứng non-admin (Director b.19 / HrAdmin b.20 / CostControl b.21) → trang Bảng cứng ≥1 dòng, KHÔNG rỗng/403.
  • Ca ÂM: leaf loại 0 HĐ phát hành → empty-state, KHÔNG 500.
  • Upload 3 purpose trên HĐ ZZTEST DaPhatHanh → 2 badge lật đúng; note b.21 có đủ {số bộ, giao CCM/NTP, nơi lưu}. C. Rủi ro: [ ] mirror mù 6-leaf (grounding cũ) — menu Hdc là 7-leaf-per-type owner vẽ, KHÔNG tự đẻ key (gotcha #84) · [ ] upload không phase-guard là HÀNH VI HIỆN TRẠNG (up được mọi phase) — W7 không siết (đó là W8-authz nếu chọn) · [ ] khai lệch b.20 gộp b.21 trong UI (tooltip/label) để user không tưởng thiếu trạm.