Files
solution-erp/.claude/workflows/runs/2026-08-08-S182-khkk-awf-mirror-ncc/sub-invest-fe-fable.md
2026-08-08 01:01:12 +07:00

14 KiB
Raw Blame History

sub-invest-fe-fable — S182 KHKK AWF-v2 mirror NCC (lens FE UI, "ko lệch")

Fable deep-pass đơn lẻ, /fable-real investigator-codebase. Đề: 8 mục sidebar "Duyệt KHKK — Nhóm 1..8" phải ra ĐÚNG trang system/approval-workflows-v2/DuyetNcc khuôn ảnh anh gửi. 0 sửa code.

§1 Trang DuyetNcc render từ đâu (route + page + KIND_CONFIG + dialog)

Route: fe-admin/src/App.tsx:79-80/system/approval-workflows-v2 + /system/approval-workflows-v2/:typeCode → cùng 1 component ApprovalWorkflowsV2Page (named export, fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx 1355 dòng).

KIND map (KHÔNG phải KIND_CONFIG object đầy đủ kiểu S29/S35 — chỉ 2 Record thưa):

  • TYPE_CODE_TO_INT :164-172 = { DuyetNcc:1, DuyetNccPhuongAn:2, Contract:3, ContractSigningPlan:10 }0 entry KhkkN*.
  • DEFAULT_CODE_BY_TYPE :173-180 = {1:'QT-DN-V2-001', 2:'QT-DN-PA-V2-001', 3:'QT-HD-V2-001', 10:'QT-KHKK-V2-001'} (chỉ gợi-ý ô Mã).
  • Nhãn hiển thị KHÔNG có bảng FE — lấy từ BE applicableTypeLabel (AwLabels.Type, ApprovalWorkflowV2AdminFeatures.cs:97 có entry type-10 "Kế hoạch ký kết HĐ").

Cơ chế render :286-353: selectedTypeInt = TYPE_CODE_TO_INT[typeCode]; unknown typeCode ⇒ undefinedcurrentType=null ⇒ render LANDING GRID mọi type (:323-343) thay vì panel. Panel thật = TypePanel :357-459 (active card + "Lịch sử versions" + nút "Tạo quy trình mới") — mỗi panel khoá vào ĐÚNG 1 applicableType, active = type.active (BE tính), history render filter !d.isActive (:419,:426).

Cột nút phải (khuôn ảnh): DefinitionCard:577-612 — Sửa tại chỗ :587 / Tạo phiên bản mới :596 / Ghim-Bỏ ghim :606 / Xoá version :610. Badge: Đang áp dụng :490 · Cho user chọn :502 · Ngưỡng CEO :506-513. Badge NS PRO/CCM per NV :560-565 (display-only suy từ role).

Dialog Sửa tại chỗ / Tạo mới: cùng component Designer:620-1354, isEdit = editTarget !== null :638; title "Sửa tại chỗ — {code} v{NN} (giữ nguyên phiên bản)" :868. Khối cam "QUYỀN DUYỆT {TÊN}" :1228-1329 = 9 checkbox map field TS (EditLevelEntry:100-123 / API LevelDto:37-62):

# Label UI Field
1 Trả về 1 Cấp trước allowReturnOneLevel
2 Trả về 1 Bước trước allowReturnOneStep
3 Trả về Người chỉ định allowReturnToAssignee
4 Trả về Drafter (mặc định) — tick sẵn allowReturnToDrafter (default true :231)
5 Chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) allowApproverEditDetails :1275
6 Chỉnh sửa Section ngân sách allowApproverEditBudget :1284
7 Duyệt thẳng Cấp cuối allowApproverSkipToFinal🔴 DISABLED khi type=10 :1292-1302 (K3: service KHKK chưa port SkipToFinal, BE ép-false)
8 Duyệt là KẾT THÚC quy trình (xanh emerald) allowApproverFinalize :1307-1315
9 Cho phép NV này XÓA phiếu (đỏ rose) allowApproverDelete :1320-1328
9 cờ gửi qua 1 hàm chung levelFlagsPayload:255-267 (2 call-site POST :820 + PUT :788 — chống silent-drop F6/F-7 S155). Rule-box xanh cuối dialog :1343-1350. Ô "Ngưỡng CEO" :944-961 render cho MỌI type (kể cả 10).

§2 8 mục KHKK sidebar hiện map đi đâu (4-chỗ pattern 16-bis)

Chỗ File:line Trạng thái
1. BE const + All MenuKeys.cs:127-134 + :248-249 8 key AwV2_KhkkN1..8 VÀO All (⇒ 32 policy)
2. BE menu-row seed DbInitializer.cs:1988-1995 label "Duyệt KHKK — Nhóm {n}", parent ApprovalWorkflowsV2 (root :1981 dưới System), order 3..10, icon FileSignature; Admin auto-grant qua SeedAdminPermissionsAsync (comment :1984-1987)
3. FE lib/menuKeys.ts mirror fe-admin:31-32 / fe-user:31-32 chỉ có AwV2_DuyetNcc(+PhuongAn)0 KhkkN. VÔ HẠI runtime: sidebar render BE-tree + resolvePath string thô; 0 call-site can('AwV2_*'); route /system/approval-workflows-v2 KHÔNG PermissionGuard (App.tsx:79-80 route trần). Optional mirror-completeness
4. FE resolvePath fe-admin/src/components/Layout.tsx:188 /^KhkkN[1-8]$//system/approval-workflows-v2/KhkkN{n} (K3 S164/S165, CHỈ nới họ AwV2_*)

Bấm vào hôm nay ra gì: route khớp App.tsx:80ApprovalWorkflowsV2Page, nhưng TYPE_CODE_TO_INT['KhkkN3'] = undefinedcurrentType=nullLANDING GRID mọi type (:323-343, header "Quy trình duyệt (Mới)" + hint "chọn loại từ menu"). CẢ 8 mục ra CÙNG landing — KHÔNG ra khuôn DuyetNcc panel. Đây chính là "lệch". (Không phải ComingSoon, không 404.)

Sibling: root AwV2 có leaf 1-2 = DuyetNcc/PhuongAn (:1982-1983); KHÔNG có leaf AwV2_Contract seed (Layout :178 chỉ phục vụ deep-link).

§3 Delta "ko lệch" — cần thêm gì + hardcode-PE-label sites

Danh tính nhóm = Code, KHÔNG phải ApplicableType. 8 workflow seed SeedKhkkGroupWorkflowsAsync (DbInitializer.cs:549-625): Code="KHKK-N{n}", CÙNG ApplicableType=10, Version=1, IsActive=true cả 8, IsUserSelectable=true, 3 trạm PRO→CCM→BGĐ sao roster Duyệt NCC prod. BE guard pin: regex ^KHKK-N([1-8])$ phải khớp plan.ApprovalGroup (ContractSigningPlanFeatures.cs:479-486). FE helper sẵn 2 app identical: khkkGroupWorkflowCode(n)='KHKK-N{n}' + khkkGroupMenuLabel(n) (fe-{admin,user}/src/types/khkk.ts:159-168). Precedent slice-by-code đã chạy ở fe-user WorkflowMatrixViewPage.tsx:84-99 (K4c: client-side filter wf.code === khkkGroupWorkflowCode(g), không thêm trục group vào queryKey).

Delta CHÍNH — 1 file fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx (page đã generic theo :typeCode; KHÔNG thêm route/menu/BE-seed):

  1. :164-172 — thêm chiều parse: const khkkKindMatch = typeCode?.match(/^KhkkN([1-8])$/)khkkGroup: 1..8 | null; selectedTypeInt = khkkGroup ? 10 : TYPE_CODE_TO_INT[typeCode] (overview query giữ nguyên applicableType=10).
  2. :299-301currentType cho kind nhóm = synthetic slice: từ types.find(t=>t.applicableType===10) lấy versions = history.filter(d => d.code === khkkGroupWorkflowCode(g)); active = versions.find(v=>v.isActive) ?? null; applicableTypeLabel = 'Duyệt KHKK — Nhóm {g}' (menu-label BE :1988) — D4 anh chọn nhãn này hay nhãn nghiệp vụ khkkGroupMenuLabel(g) = "{g}. Thiết bị, máy móc…" (đề xuất: title menu-label + phụ đề nghiệp vụ).
  3. TypePanel:357 + Designer:620 — prop mới lockedCode?: string: Designer defaultCode → lockedCode ?? DEFAULT_CODE_BY_TYPE[...] (:650-651), ô Mã disabled={isEdit || !!lockedCode} (:927), POST gửi lockedCode ?? code (:798). Thiếu khoá này thì "Tạo quy trình mới" từ panel nhóm 3 với mã tự gõ sẽ đẻ version rơi khỏi slice → biến mất khỏi panel (và không được guard :482 nhận là workflow nhóm).
  4. Không đổi gì khác của khuôn: 4 nút phải, badge, Lịch sử versions, dialog 9-checkbox, rule-box — dùng nguyên TypePanel/DefinitionCard/Designer (đúng "ko lệch"). SkipToFinal đã tự disable cho type-10 (:1292-1302) — 8 kind thừa hưởng vì cùng applicableType=10.

Hardcode-label sites trong khuôn chung (đánh dấu — KHÔNG tự quyết):

  • :1275 "Cho phép chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) lúc đang duyệt" — từ vựng PE. KHKK service KHÔNG đọc cờ này (0-hit AllowApproverEditDetails trong ContractSigningPlan*.cs).
  • :1284 "Section ngân sách" — tương tự, 0-hit AllowApproverEditBudget.
  • :944-961 ô "Ngưỡng giá trị gói CEO" render mọi type — KHKK service CỐ Ý KHÔNG port CeoApprovalThreshold (ContractSigningPlanWorkflowService.cs:46-47: NĐUQ = human-gate vĩnh viễn, OG-3) ⇒ nhập cho kind KHKK = config-lie class #78. Khuôn disable-có-tooltip sẵn ở :1292.
  • Badge "✎ NS PRO/NS CCM" (:560-565) — suy từ role PE-budget; hiển thị trên panel KHKK như đang hiển thị cho type-10 hiện nay.
  • Răng thật của 9 checkbox với KHKK hôm nay: CHỈ #8 AllowApproverFinalize sống (ContractSigningPlanWorkflowService.cs:276); 4 cờ Trả-về không đọc — Trả lại KHKK = TOÀN PHẦN luôn-được (:48,:354-362); Delete/EditDetails/EditBudget/SkipToFinal: 0-hit. D3: giữ nguyên khuôn (đúng "ko lệch", như panel ContractSigningPlan đang render) hay disable 7 ô chết theo khuôn SkipToFinal — anh/lead chốt; mặc định nghiêng GIỮ (uniform, cờ nằm chờ port).

§4 fe-user — IsUserSelectable tại create KHKK + designer admin-only proof

  • Tạo phiếu KHKK KHÔNG có dropdown chọn quy trình — workflow GÁN CỨNG theo nhóm (owner S174): fe-user/src/pages/khkk/KhkkCreatePage.tsx:96-116 query applicableType=10filter w.isUserSelectable :104autoWorkflow = find(code === 'KHKK-N{group}') :113-116; thiếu mã ⇒ CHẶN tạo + báo "Chưa có quy trình KHKK-N{n}" (:128,:155). fe-admin/src/pages/khkk/KhkkCreatePage.tsx mirror cùng khuôn (:97-100). ⇒ Bỏ ghim (IsUserSelectable=false) 1 workflow nhóm = CHẶN TẠO PHIẾU nhóm đó ở cả 2 app — nút Ghim/Bỏ ghim trên 8 panel mới có hệ quả nghiệp vụ thật.
  • Consumer thứ 2 fe-user/src/pages/khkk/KhkkDetailPage.tsx:214-227 = bridge KHKK→HĐ, query applicableType=3 lọc isUserSelectable && isActive — không liên quan nhóm.
  • Designer admin-only — CHỨNG: grep approval-workflows-v2 toàn fe-user/src = chỉ API-consumer (Create/Detail/Matrix/Proposal/WorkflowApp) — 0 page designer, 0 route /system/approval-workflows-v2, Layout fe-user 0 nhánh AwV2_* (grep 0-hit ngoài lib/menuKeys.ts:31-32 const chết). fe-user chỉ có view read-only pages/pe/WorkflowMatrixViewPage.tsx (/khkk/workflow-matrix?type=10&group=n, K4c slice cùng convention code).

§5 Wave FE đề xuất + acceptance

W-FE1 (fe-admin, 1 file): ApprovalWorkflowsV2Page.tsx — 4 chạm §3 (parse kind :164-172 · synthetic slice :299-301 · lockedCode xuyên TypePanel:345-350/:357/:447-455Designer:620/:650/:798/:927 · title). Import từ types/khkk.ts (đã có sẵn trong fe-admin). Ước ~60-90 dòng diff. W-FE2 (optional): fe-{admin,user}/src/lib/menuKeys.ts +8 const AwV2_KhkkN* (mirror-completeness, 0 hệ quả runtime). SHA-mirror 2 app: KHÔNG áp dụng — trang system Designer là fe-admin-only (chứng §4); types/khkk.ts 2 app đã identical sẵn, không chạm. BE: 0 bắt buộc cho khuôn hiển thị; D1 (khuyến nghị làm cùng wave): ApprovalWorkflowV2AdminFeatures.cs:339-343 deactivate-per-TYPE — sửa thành per-Code khi code khớp ^KHKK-N[1-8]$ (hoặc per-Code toàn cục — semantic đổi, anh chốt).

Acceptance đo được:

  1. Login admin → sidebar Hệ thống Quy trình duyệt (Mới) hiện 10 leaf; bấm "Duyệt KHKK — Nhóm 3" → URL /system/approval-workflows-v2/KhkkN3 → header "Quy trình duyệt (Mới): Duyệt KHKK — Nhóm 3" + card KHKK-N3 v01 badge "Đang áp dụng"+"Cho user chọn" + 3 Bước (PRO 4NV+TP·CCM 4NV+TP·BGĐ CEO) + cột 4 nút phải + "Lịch sử versions"/"Tạo quy trình mới". 8/8 leaf → 8 slice khác nhau, hết landing grid.
  2. "Sửa tại chỗ" → dialog title "Sửa tại chỗ — KHKK-N3 v01 (giữ nguyên phiên bản)", per-NV khối cam "QUYỀN DUYỆT …" 9 checkbox 2 cột, "Trả về Drafter (mặc định)" tick sẵn, dòng xanh KẾT THÚC + dòng đỏ XÓA phiếu, SkipToFinal mờ + tooltip, rule-box cuối.
  3. "Tạo quy trình mới" từ panel nhóm: ô Mã khoá KHKK-N3 → lưu ra v02 nằm ĐÚNG panel nhóm 3; (nếu D1 làm) badge "Đang áp dụng" 7 nhóm khác KHÔNG mất.
  4. Regression: /DuyetNcc /DuyetNccPhuongAn /Contract /ContractSigningPlan render như cũ; fe-user tạo phiếu nhóm 3 vẫn auto-pin KHKK-N3 (không đổi API).

§6 Surprises ngoài đề

  1. 🔴 Panel ContractSigningPlan (đích của Khkk_WfView admin, Layout.tsx:40) hôm nay GIẤU 7/8 workflow nhóm: BE Active = FirstOrDefault(IsActive) per-type (ApprovalWorkflowV2AdminFeatures.cs:210, tie-break Version DESC không xác định giữa 8 bản v1) + FE history filter !d.isActive (:419,:426) ⇒ 8 bản đều active thì 1 hiện card, 7 KHÔNG hiện đâu cả. Slice-by-code của wave này vá đúng bệnh; cân nhắc re-point Khkk_WfView hoặc để panel aggregate liệt theo code.
  2. 🔴 Bom deactivate-per-TYPE (:339-343): bấm "Tạo quy trình mới"/"Lưu + kích hoạt" cho BẤT KỲ mã nào thuộc type-10 ⇒ IsActive=false CẢ 8 nhóm (seeder không re-set — idempotent per-Code :610 skip). Runtime tạo phiếu KHÔNG hỏng (pin theo IsUserSelectable, guard type EnsureWorkflowTypeAsync:307-318 không đòi IsActive) nhưng badge/active-slice sai toàn bộ. = D1.
  3. Seeder KHKK resolve người duyệt THEO EMAIL, thiếu user ⇒ bỏ slot/cấp/bước + log warning (DbInitializer.cs:544-548,:597-600) — DB dev thiếu người thật thì 8 panel có thể hiện bước rỗng/thiếu NV; không phải bug FE.
  4. AwV2_Contract không có menu-leaf seed (chỉ deep-link Layout.tsx:178) — sibling lệch nhẹ, không thuộc scope.
  5. Route FE /system/approval-workflows-v2 KHÔNG PermissionGuard (App.tsx:79-80) — 2-tầng gotcha #82: hiển thị dựa menu-grant, authz thật ở BE controller.