6.0 KiB
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
- Gate 5-anchor (§③-A).
- 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ệ PEComparisonTable=4;ContractAttachment.cs:5-11); (b) 🔴 thêm paramtypevàoListContractsQuery+ controller (review F-C1/F-05 —GET /api/contractsHIỆN 0 CÓtype,ContractsController.cs:16-24chỉ phase/supplierId/projectId): thêm[FromQuery] ContractType? type+ filter handlerContractFeatures.cs:305-307— KHÔNG lọc client-sidepageSize:100(trần 100/loại). (c)ContractListItemDto+2 cờ derivehasSealedCopy/hasInitialSignedtrong projection (DTO hiện 12 field:323-327KHÔNG trả attachments; 🔴 badge "Đã ký GĐ" derive từInitialSigned/SealedCopy, KHÔNG từScannedSigned— review F-C6:ScannedSigned=2nghĩa "scan có chữ ký NCC ở DangInKy", proxy-predicate sai vai b.19 GĐ, gotcha #71). - FE ×2 app: page
HardCopiesPage(khuônMyContractsPage.tsx— ĐÃ đọc đủtype/phaseparams:29,:34-35): route/hard-copies?type=N→ listGET /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.tsxroute +Layout.tsxstaticMap re-point 7 dòngHdc_(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. - 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ùngContracts.{Read,Update}— Contracts ∈ All:168, policy sống sẵn.) - 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 paramtypeLỌC thật, không hiện trùng 7 leaf) ·HardCopyBadge_DerivesFromSealedCopyAttachment. dotnet testPASS 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
typelọ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 itemphase==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.