14 KiB
SPEC TỔNG QUÁT v2 — Chuỗi 4 GĐ theo SOL-CCM-SP-002 (B1a + B1b review, lead kết tinh @S164)
v1 = kết tinh từ
sub-invest-fable-b1a.md(spot-check 4/4). v2 = v1 + 14 vá từ B1b (b1b-review-synthesis.md— 3 lane reviewer 41 finding thô; lens2 xác nhận 6/6 claim chịu-lực ĐÚNG ⇒ nền giữ, vỏ vá). Yêu cầu gốc:draft-yeu-cau-owner.md.
① Tính năng / đề bài
Chuỗi 4 GĐ per-gói-thầu theo SOL-CCM-SP-002: GĐ1 Duyệt NCC (PE = CHA, đã chạy) → GĐ2 KHKK = duyệt HẠNG MỤC theo 8 NHÓM DUYỆT (trình ký số trạm-by-trạm y Duyệt NCC; ký nháy = mực trên bản cứng GĐ4) → GĐ3 điều khoản → GĐ4 bản cứng 1-mốc. Scope 5 mảnh: master-data danh mục · sidebar 8 nhóm × 6 leaf · cây mirror sidebar · admin thu gọn + 8 designer · dry-run E2E. 🔴 Luật khuôn: GIỐNG NHAU — CHỈ KHÁC FORM — ĐỪNG CHẾ THÊM. 🔸 Khai thẳng (B1b l3-F4): đợt này 8 nhóm = đơn vị HIỂN THỊ + luồng duyệt, KHÔNG phải đơn vị authz — mọi endpoint KHKK giữ 1 bộ policy KeHoachKyKet.{R,C,U,D} per-action (ContractSigningPlansController.cs:28+); authz-per-nhóm nếu anh muốn = wave riêng sau.
② Cách implement — 9 quyết định (v2; B3-A0b sửa đếm)
- [OG-1] Cardinality "đưa vào loại HĐ" default GỘP theo cặp (NCC × dòng-danh-mục) — N line chung
ContractId(hợp khuônContractSigningPlanLine.cs:25, index không-uniqueConfiguration:26); đảo 1-1 được cùng máy. - Phiếu KHKK = per NHÓM (1 phiếu = 1 nhóm, N hạng mục). Lines per-hạng-mục: Mig +
CatalogEntryId Guid?+TenHangMuc, UNIQUE → (PlanId, SupplierId, CatalogEntryId) filtered. 🔴 +2 vá B1b: (a) đổi rào 1-PE-1-phiếu (ContractSigningPlanFeatures.cs:342-347+ picker:1201-1202) → (PeId, ApprovalGroup) [OG-7] — FE tree đã chịu N-phiếu/PE (usePipelineStages.ts:110-121); (b) bất-biến tiền:PeReferenceAmount= snapshot per-NCC-toàn-phiếu (Line.cs:6,:16-20) — repurpose per-hạng-mục PHẢI định nghĩa lại semantics + Σ trước == Σ sau mig, acceptance rà 18 consumer (17 grep-verified cụm-1 +Features.cs:712-713Σ Proposed — số chốt theo cụm-1 vá-8; B3-A-x). - Workflow = 1 type-10 + 8 Code
KHKK-N1..N8; only-ONE-active VÔ HẠI (chứng 2 mắt xích l2: create chỉ check type:209-220; overview không lọc IsActive:117-134, POST không đụng IsUserSelectable:341-353). 🔴 CộtApprovalGroupK2 là CHỦ DUY NHẤT (l3-F6) — K3 chỉ đọc (map Code↔group + validator phiếu-pin-đúng-nhóm). - Vai catalog = 2 tầng: danh mục lưu chuỗi vai TEXT (in khối ký GĐ4) ⟂ workflow Levels lưu NGƯỜI THẬT. 🔸 Khai limitation (l1-F12): TEXT không ràng buộc máy với người-duyệt-thật — chấp nhận đợt này, khai trong doc K1.
- NĐUQ = cột THAM KHẢO text [OG-3 nâng cấp]; enforce máy = KHÔNG BAO GIỜ (human-gate vĩnh viễn — a Chương quyết; B3-A0a: xóa nốt vế "wave opt-in sau" sót ở đây).
- Menu: GIỮ
Khkk_G1+ 6 leaf cũ, đổi LABEL = nhóm 1 (labelBackfill khuôn S155). Nhóm 2-8 = 42 leaf MỚI + 7 group-key mới (49 row menu mới; 48 leaf TỔNG) — sửa số l1-F8/l2-F5. 🔴 Phiếu cũ: backfillApprovalGroup = 1(l3-F5 — mirror bàiEvaluationTypekhông rỗng; acceptance đếmCOUNT(phiếu) == Σ COUNT 8 folder). - Admin ẩn = fe-admin
isAdminHiddenMỞ RỘNG TẬP KEY TƯỜNG MINH (giữ blacklist — KHÔNG đảo whitelist, tránh đổi default menu-mới; vá hội tụ l1-F2 ⨯ l3-F1/F2): ẩn thêm {Dashboard?→giữ, các root nghiệp vụ:DuyetNcc-root keys,KeHoachKyKet, HR/Office/Personal,Contracts(đã ẩnCt_*),HardCopies,Forms,Reports,Catalogs/Master,Workflows(:1795),PeWorkflows(:1798)} — CHỪA {System+ 4 leaf quản trị +ApprovalWorkflowsV2(:1801, con System) + subtree}. Danh sách key CHÍNH XÁC = việc B2-cụm-2 liệt từDbInitializerseed; acceptance = liệt kê đúng N mục lá còn thấy (l3-F2). - Nguồn danh mục: file đĩa
QUY_TRINH/QT TRINH KY HOP DONG TP-NCC.docx= ĐÚNG SOL-CCM-SP-002.v01 (mã ở word/header1.xml) NHƯNG bản 6-trang KHÔNG chứa bảng (lead đo). Nguồn bảng = 7 ảnh anh gửi @S164 (trang 5-9, trọn 5.1 A/B/C + 5.2 CEO) → transcribe → [OG-6] anh soát bảng seed trước land (hoặc anh gửi file 15-trang). "/" trong ô vai = OR-of-N. - 🆕 KHÓA BUDGET TẠI FINALIZE (owner @S164; văn SỬA theo B3-A4 — thiết kế THẬT do cụm-1/cụm-3 chốt, cha theo con): khi phiếu KHKK finalize tại CCM (a Chương) HOẶC CEO duyệt final ⇒ số KHÓA. 🔴 KHÔNG chép khuôn 11-cột Mig 67 — máy freeze per-line ĐÃ CÓ SẴN:
PeReferenceAmountcopy-at-create (Line.cs:17-20) +ApprovedAmount ??=tại choke-point (WorkflowService.cs:301-305, 1-site-DaDuyet verified:129/:278/:289-292) ⇒ việc còn lại = phase-guard line-edit sau DaDuyet (K2) + test-khóa 1-site (K2) + phép đo 2-nhánh TRƯỢT-ĐƯỢC (K8 — sửa nguồn → reload → số KHÔNG đổi, đo cả nhánh CCM-finalize lẫn CEO) + cột runtimeEndedByLevelFinalize(K2 item-6 đẻ, K3 tiêu thụ — 3 spec cùng 1 cột ✓). Người quyết = TP.CCM Phan Văn Chương. Né #81-EXT: sweep site gán Phase — đã liệt 4 site, GỠ nghi ánWorkflowService.cs:342(comment đọc-đủ-khối: CỐ Ý không có site gán-qua-biến).
③ Wave K1-K8 (v2 — PRE-gate + acceptance có răng)
Cụm 1 (nền):
- K1 Master-data danh mục — PRE: [OG-6]. BE entity+config+CQRS+controller per-action policy; Mig 70 (3-file rule) filtered-unique Code #57; seed per-Code idempotent; FE leaf Catalogs ×2 app. DoD: N dòng khớp ~85±5, lệch giải trình từng dòng (đối chứng ảnh) · tên VN+EN đủ · CRUD 200 · seed ×2 không nhân row · test ≥590+k, khai k.
- K2 Phiếu mang hạng-mục + nhóm — PRE: [OG-1] + [OG-7]. Mig 71 (3-file): plan +
ApprovalGroup intbackfill=1; Lines +2 cột + đổi UNIQUE; đổi rào (PeId)→(PeId,ApprovalGroup) 2 site:342-347/:1201-1202; list-query +paramgroup(ListContractSigningPlansQuery:622-625— K2 chủ, l3-F3) + DTOContractSigningPlanListItemDto:34+approvalGroup(sửa citation l2-F4) + FE types. DoD: phiếu nhóm 2 CÙNG PE → 201 · line khác nhóm → 409 · phiếu cũ ở folder nhóm 1,COUNT == Σ 8 folder· Σ tiền trước == sau mig · bảng rà 16 consumer ĐÃ-XỬ/KHÔNG-ĐỔI · test ≥590+k. - K3 8 workflow nhóm + designer — PRE: [OG-2] gật roster + verify đội hình LIVE (không phải archived). Seed 8 workflow
KHKK-N1..N8type-10 (khuôn:171) + validator pin-đúng-nhóm; 8 menu-con dướiApprovalWorkflowsV2+ mởresolvePathfe-adminLayout.tsx:166-172nhận code mới (leaf lạ rớt im lặng #50) + route designer per-workflow. DoD: API trả 8 · sidebar admin đếm được 8 designer, mở designer nhóm n load đúngKHKK-N{n}(l3-F10) · phiếu nhóm 3 pin workflow nhóm 5 → 409 · duyệt trọn trạm 1 phiếu → DaDuyet · test ≥590+k.
Cụm 2 (UI): (l3-F8: tách phụ thuộc thật — K4a chạy song song cụm 1 được)
- K4a Menu+grant (0 phụ thuộc): 49 row menu + grant 3 SITE (
:1780seed ·SeedKeHoachKyKetAccessAsync:2415·KhkkKeys():2185— site-3 l2-F6, comment:2204"quên 1 bên = quyền lệch"). K4b 4 leaf list + Thao tác (chờ K2 param). K4c leaf WfView/Create (chờ K3): WfView mang?group=lọc theo workflow Code nhóm (hiệnWorkflowMatrixViewPage:52-71chỉ lọc type — l2-F3). DoD: 48 leaf render đúng · leaf lọc đúng group kể cả WfView/Create · 2 chiều theo bảng kỳ-vọng 13 role (seeder hiện phủ 7 vai:2671-2675— 6 vai còn lại khai kỳ vọng TRƯỚC, l3-F13) · key cũ 0 mồ côi. - K5 Cây sub-folder: GĐ2 group-by
approvalGroup· GĐ3/4 group-byc.type(đã có:240); thêm tầnggroupsvàoPipelineStage(mở rộng khuôn — bẫycontentphải truyềncounttay:74-75). DoD: sub-folder khớp sidebar · 4 file pipeline SHA-identical ×2 app (đối chiếu cụm-2 — usePipelineStages · PipelineStageFolders · PipelineTreePanel · trang nhúng; B3 sửa đếm) · build ×2 PASS. - K6 Admin thu gọn (độc lập NHƯNG l3-F9: kéo sớm TRƯỚC K3 thì 8 designer chưa tồn tại — nếu kéo sớm, acceptance "đếm 8 designer" dời sang sau K3): QĐ7 tập-key tường minh. DoD: liệt kê đúng N mục lá còn thấy (đủ tên, dư/thiếu 1 = FAIL) · fe-user KHÔNG đổi =
git diff --stat <base>..<head> -- fe-user/src= 0 file (KHÔNG đo bundle-hash #69 — l3-F12).
Cụm 3 (nối + chạy):
- K7 Cầu KHKK→HĐ — RE-ĐO 3 NẤC (l3-F11): (a) cột/index ✓ có (
Line.cs:25+Config:26); (b) write-site gánContractId= 0 hiện tại (lead xác nhận grep); (c) test E2E — ⇒ trạng thái NỬA-LAND, K7 build phần (b)+(c). Bridge đọcApprovedAmountper-Line + gộp theo OG-1 + pin workflow V2 + chọnContractTypelúc bắc cầu (ai chọn = câu nghiệp vụ, mặc định người bấm chọn trong dialog — l1-F15). DoD: KHKK DaDuyet → HĐ đúng số lượng OG-1 ·giaTri == Σ ApprovedAmountphạm-vi-gộp khai rõ, null → chặn với message (l1-F6) · approver V2 thấy + inbox · test bridge ≥590+k. - K8 DRY-RUN E2E từ
PE/2026/A/049. DoD (+3 dòng l3-F16): mọi bản ghi mới mang tiền tố ZZTEST · danh sách bản-ghi-không-xoá-được + cách sống chung (HĐ DaPhatHanh guard numeric) · tắt/dặn-team notification trước khi chạy · toàn trình hết trạm → HĐ · cây đủ 4 GĐ · screenshot + mã phiếu thật.
Mọi wave: cicd PASS; byte-verify #77 CHỈ wave có FE-ship — wave BE-only = smoke API (l3-F17); mig = 3-file rule.
④ [OWNER-GATE] — 8 hàng, cập nhật sống (B3-A0b sửa đếm)
| OG | Câu hỏi | Trạng thái | Chặn wave |
|---|---|---|---|
| OG-1 | Gộp HĐ | ✅ CHỐT @S164 (verbatim: "thiết kế gộp nhưng giao diện thì cho 1-1 (Khi user đổi ý thì mình làm luôn)") ⇒ schema GỘP-capable (N line chung ContractId) + UI default 1-1 (bridge = 1 hàm nhận list-line, UI truyền 1 line; bung gộp = FE sau, 0 đổi máy) | K2, K7 |
| OG-2 | Roster 8 workflow | ✅ CHỐT @S164 ("Đúng đội hình cũ") — map đội hình QT-DN-V2-001, verify bản LIVE (không archived) trước seed |
K3 |
| OG-7 | 1 PE → N phiếu | ✅ CHỐT @S164 ("PE cho nhiều, nhưng… rào hiển thị 1-1 lại — User cần thì bung ra") ⇒ BE mở N-phiếu/PE (đổi rào 2 site :342/:1201 → (PeId, ApprovalGroup)) + UI default hướng-1: PE có phiếu rồi → hiện phiếu + nút phụ "Tạo thêm cho nhóm khác" (thay 409 chặn hẳn) | K2 |
| OG-3 | NĐUQ | ✅ CHỐT @S164 ("Đồng ý. Vẫn như trước - các quyền kiểu chuyển CEO các thứ y chang vậy luôn, thì ko cần quan tâm mức duyệt, vì a Chương sẽ quyết định là cái đó cần CEO ko hay là anh ấy đc duyệt.") ⇒ NĐUQ = ghi chú tham khảo; cơ chế lên-CEO = TÁI DÙNG level-finalize sẵn có của V2 (người trạm CCM — Phan Văn Chương — tự chọn "kết thúc tại cấp" hay trình tiếp, y PE đang chạy: EndedByLevelFinalize runtime + checkbox opt-in S97) — 🔄 SỬA @B2c1 (mâu thuẫn spec↔code, evidence fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx:151-152 KHKK_EARLY_FINALIZE_NOTE "backend chưa xử lý đường duyệt sớm"): cơ chế = tái dùng KHUÔN level-finalize NHƯNG BE KHKK CHƯA port nhánh ⇒ K3 có 3 việc code: (a) port nhánh finalize vào ContractSigningPlanWorkflowService (b) cột runtime EndedByLevelFinalize né #78 (forward-provision vào Mig 71 ⇒ K3 0-mig) (c) gỡ khóa 2 ô Designer FE. Seed AllowApproverFinalize=true cấp CCM-Chương CHỈ SAU khi (a) land — kẻo config-lie #78 |
— |
| OG-6 | Soát bảng transcribe 86 dòng (gõ tay từ ảnh; 4 ô ⚠️ cuối file) HOẶC anh gửi file 15-trang | 🔄 ĐỔI NHÃN soát-SAU @PAUSE-2 (mandate chạy-1-mạch) — bảng admin-editable qua Admin CRUD; seeder skip-if-exists nên sửa-tay CRUD sống qua restart (update-branch sẽ đè mất sửa của anh — cố ý KHÔNG có); 4 ô ⚠️ chờ anh soát SAU trên UI | — (HẾT chặn K1) |
| OG-8 | Authz per-nhóm | ✅ CHỐT @S164 ("Đúng chính xác. Cho hiển thị, phân quyền tính sau.") — đợt này 8 nhóm = hiển thị + luồng duyệt; XEM/TẠO chung bộ khóa module; authz-per-nhóm = wave sau khi anh gọi | — |
| OG-5 | Extract mơ hồ → hỏi khi gặp | mở | K1 |
| OG-9 | Bật lại duyệt-sớm KHKK = ĐẢO phán quyết "PHÁ VỠ" @S155 | ✅ CHỐT @S164 ("đúng, chỗ budget chốt chặn là a Chương, anh ấy sẽ quyết cái j cần quyền CEO và cái j anh ấy đc duyệt") — K3 việc (a)-(e) HẾT CHẶN: port nhánh finalize + cột runtime + gỡ khóa FE + sweep ép-false ≥4 site + sửa comment/panel/HANDOFF khai supersede-S155 | K3 mở |
🔴 OG-3 NÂNG CẤP @S164 (cùng lời chốt OG-9): "vậy bài toán trần budget xem như ĐÓNG. Vì người chốt chặn budget là anh Chương." ⇒ NĐUQ/ngưỡng tiền = HUMAN-GATE VĨNH VIỄN (TP.CCM Phan Văn Chương quyết cái gì cần CEO) — XÓA ý "enforce máy = wave opt-in sau" khỏi mọi spec; cột NĐUQ trên danh mục thuần ghi chú. Không còn wave threshold nào trong backlog.
BACKLOG giai-đoạn-sau (owner @S164, không làm đợt này): (1) authz-per-nhóm KHKK (OG-8) · (2) phiếu điều chỉnh/thay thế cho chứng từ đã duyệt — khuôn amendment ERP, áp PE/KHKK/HĐ ("Nếu xóa thì có thể làm 1 tính năng điều chỉnh phiếu để cập nhật điều chỉnh/thay thế - ko xóa phiếu đã duyệt. Cái này để giai đoạn sau.") · (3) bung-gộp UI (OG-1/OG-7 nửa sau — máy đã sẵn).
END spec v2 — 8 wave · 9 quyết định · OG đủ · backlog 3 mục giai-đoạn-sau.