diff --git a/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx b/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx index e2fa3bc..dd635ff 100644 --- a/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx +++ b/fe-admin/src/pages/khkk/KhkkWorkflowPanel.tsx @@ -12,9 +12,12 @@ // owner ĐẢO phán quyết S155 (@S164 — "a Chương quyết cái gì cần CEO") ⇒ BE **đã port nhánh finalize cho // KHKK ở K3 cùng wave**, và Designer type-10 đã GỠ rào (`ApprovalWorkflowsV2Page.tsx` — hằng // `earlyFinalizeLocked` xoá hẳn, 2 ô tick sống lại). Đừng đọc file này thành "BE không hỗ trợ". -// Panel VẪN không có control riêng cho đường kết-thúc-sớm — nhưng vì lý do KHÁC HẲN: `AllowApproverFinalize` -// là cấu-hình PER-CẤP do admin tick ở Designer, BE đọc lúc duyệt để quyết dừng hay trình tiếp CEO. Người -// duyệt chỉ bấm "Duyệt" như thường; không có gì để panel render thêm. +// 🔴 [F-1 S166 — SUPERSEDE tiếp] Câu cũ "panel không có control riêng cho đường kết-thúc-sớm" HẾT hiệu lực: +// gate-K3 chấm nó là F-1 MAJOR (BE `?? true` mà 0 UI gửi false ⇒ trạm CEO chỉ đi được bằng admin bỏ cờ +// Designer). Nay panel CÓ ô-tích "Cấp này KẾT THÚC" theo khuôn PE-LIVE S97 = OPT-IN (G-1 reviewer: +// PE `useState(false)` :61 — owner đã ĐẢO opt-out S96 → opt-in S97; nhãn cũ "khuôn S96" trích trúng +// chú-thích chết). Default KHÔNG tick ⇒ gửi false ⇒ trình tiếp; TÍCH ⇒ true ⇒ BE kết thúc tại cấp. +// Ratify default treo 1 câu ở sổ CHỜ-ANH (lật = 1 edit); hành vi mặc định trạm CCM ĐỔI so trước-F-1. // // 🔴 KHÁC PE Ở CHỖ ĐẮT NHẤT: PE nhận `approvalFlow` đã có `status` Done/Current/Pending do BE precompute. // KHKK BE trả CÂY THÔ (`ContractSigningPlanFeatures.cs:112-117` + `:105-110`) — KHÔNG có `status`. @@ -79,6 +82,13 @@ export function KhkkWorkflowPanel({ const { user } = useAuth() const [action, setAction] = useState(null) const [comment, setComment] = useState('') + // [F-1 S166 · G-1 reviewer] Ô-tích level-finalize theo khuôn PE-LIVE S97 = OPT-IN + // (`PeWorkflowPanel.tsx:61` useState(false) — owner đảo opt-out S96 → opt-in S97 "cái này option + // thôi"; đừng trích comment S96 chết ở `:769-771` file đó). Default UNCHECKED = duyệt xong TRÌNH + // TIẾP cấp sau (fail-mode an toàn: không đọc thì phiếu vẫn lên CEO); TÍCH mới KẾT THÚC tại cấp. + // ⚠️ Đổi hành vi mặc định trạm CCM so trước-F-1 (BE `?? true` auto-finalize) — treo 1 câu ratify + // owner ở sổ CHỜ-ANH; lật lại = đổi 1 chữ false→true + 2 nhãn dưới. + const [applyLevelFinalize, setApplyLevelFinalize] = useState(false) const phase = plan.phase as KhkkPhaseValue const steps = plan.workflowSteps ?? [] @@ -125,6 +135,13 @@ export function KhkkWorkflowPanel({ const actorInLevel = isAdmin || actorIsCurrentApprover const blockedByLevel = isWaiting && !actorInLevel + // [F-1 S166] Ô-tích opt-out hiện theo cờ của ĐÚNG level BE sẽ chấm — mirror `ResolveActingLevel` + // (`ContractSigningPlanWorkflowService.cs:455-469`): own-level TRƯỚC (kể cả Admin có slot riêng), + // Admin không có own ⇒ level ĐẦU nhóm cùng-order. Soi cờ của level KHÁC là hiện ô-tích nói dối. + const actingLevel = currentLevels.find(l => l.approverUserId === user?.id) + ?? (isAdmin ? currentLevels[0] : undefined) + const approverFinalizeEligible = actingLevel?.allowApproverFinalize === true + // Nút "Gửi duyệt": guard BE là `CreatedBy == actor ∨ DeptManager(cùng phòng) ∨ Admin` // (spec W3 §②-5). FE KHÔNG suy được vế DeptManager — `UserInfo` chỉ có {id,email,fullName,roles} // (`types/auth.ts:1-6`), không có phòng ban. CỐ Ý **không ẩn** nút theo vai: ẩn sẽ giấu nút với @@ -134,8 +151,16 @@ export function KhkkWorkflowPanel({ const transition = useMutation({ mutationFn: async (a: KhkkTransitionActionValue) => { - // 🔴 BODY LITERAL theo hợp-đồng LEAD chốt: đúng 2 field `action` + `comment`. - const body: KhkkTransitionInput = { action: a, comment: comment.trim() || null } + // 🔴 BODY theo hợp-đồng LEAD chốt W3 (`action` + `comment`) + [F-1 S166] field thứ 3 + // `applyLevelFinalize` CHỈ đính kèm khi approve: cấp có cờ gửi giá trị ô-tích, cấp thường + // gửi true = no-op (mirror PE `PeWorkflowPanel.tsx:259`; BE `bool? = null` ⇒ absent = true). + const body: KhkkTransitionInput = { + action: a, + comment: comment.trim() || null, + ...(a === KhkkTransitionAction.Approve + ? { applyLevelFinalize: approverFinalizeEligible ? applyLevelFinalize : true } + : {}), + } const res = await api.post( `/contract-signing-plans/${plan.id}/transitions`, body, @@ -295,7 +320,15 @@ export function KhkkWorkflowPanel({