[CLAUDE] Contract: K3 SP-002 8 workflow nhom KHKK-N1-8 + port level-finalize + designer (0-mig) + 5 test
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m10s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m10s
Seed 8 workflow type-10 theo ROSTER LIVE prod (QT-DN-V2-001, lead verify tay @SQLEXPRESS: 3 tram PRO->CCM->CEO, 11 nguoi, resolve EMAIL, idempotent per-Code; co Finalize CHI cap chuong.phan). Port nhanh level-finalize mirror PE :866-876 (EndedByLevelFinalize K2-de-K3-tieu + ApplyApprovedValuesOnFinalize truoc Phase; opt-out applyLevelFinalize 3-tang interface/service/controller, default true). Sweep ep-false va-3: GO CREATE ep-false Finalize type-10 (OG-9 supersede PHA-VO S155); GIU PUT-Conflict (luat versioning CHUNG - spec liet nham, khai lech); GIU ep-false SkipToFinal type-10 (K3-test O-1 chong config-lie mới) + FE disable o SkipToFinal cho KHKK (gate-K3 F-2). Rao (v) pin-dung-nhom regex KHKK-N[1-8]. +8 MenuKeys AwV2_KhkkN* (All 56->64, policies 224->256 may-dem) + 8 menu-row + resolvePath fe-admin phep-am Khkk_ 0-hit. 5 test K3 (T-a..T-e, anti-tautology). Gate K3 PASS-WITH-FLAGS 8f/0-blocker (sub-reviewer-gate-k3.md): F-1 checkbox opt-out man duyet = KHAI HANDOFF dau-viec-#1 lan sau (tram CEO tam thoi qua Designer bo co); F-2 va; F-3 dong truoc do. Suite 614/0 (45D+569I). K3-BE 2 luot engine-exit -> che nho + lead-solo (bai: task-hep-Write-som song). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -173,6 +173,14 @@ function resolvePath(key: string): string | null {
|
||||
if (code === 'DuyetNcc' || code === 'DuyetNccPhuongAn' || code === 'Contract') {
|
||||
return `/system/approval-workflows-v2/${code}`
|
||||
}
|
||||
// [K3 S164/S165] Họ MỚI `AwV2_KhkkN1..N8` — 8 quy trình duyệt theo NHÓM của
|
||||
// Kế hoạch ký kết HĐ (mỗi nhóm 1 đội hình 3 trạm PRO → CCM → CEO). Đây là "chỗ
|
||||
// thứ 4" Pattern 16-bis: BE seed leaf mà thiếu nhánh này thì `resolvePath` trả
|
||||
// null ⇒ MenuLeaf DROP IM LẶNG khỏi sidebar (gotcha #50).
|
||||
// 🔴 CHỈ nới họ `AwV2_*` (Designer, fe-admin CÓ trang). KHÔNG nới họ `Khkk_*`
|
||||
// — fe-admin cố ý KHÔNG có `WorkflowMatrixViewPage` (xem ghi chú khối `Pe_*`
|
||||
// bên trên); nới ở đó = đẻ leaf chết rơi vào catch-all "chưa được build".
|
||||
if (/^KhkkN[1-8]$/.test(code)) return `/system/approval-workflows-v2/${code}`
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
@ -4,9 +4,17 @@
|
||||
// Khuôn: `components/pe/PeWorkflowPanel.tsx` — GIỮ sơ đồ Bước→Cấp ✓/●/○, banner "đến lượt bạn"
|
||||
// (`blockedByV2Level` :109-114), 3 nút màu (emerald/amber/red), Dialog + ô ý kiến.
|
||||
// ĐƠN-GIẢN-HOÁ CÓ CHỦ ĐÍCH (spec W3 §②-3): KHÔNG price-picker, KHÔNG ngân sách, KHÔNG file-khi-duyệt,
|
||||
// KHÔNG 4 return-mode (W3 = TraLai toàn phần), KHÔNG 2 đường kết-thúc-sớm (AllowApproverFinalize /
|
||||
// CeoApprovalThreshold — anh phán "PHÁ VỠ" S155; W1 đã rào ở Designer type-10). Chốt giá per-Line là
|
||||
// việc BE làm ở finalize choke-point, KHÔNG phải việc của panel này.
|
||||
// KHÔNG 4 return-mode (W3 = TraLai toàn phần). Chốt giá per-Line là việc BE làm ở finalize
|
||||
// choke-point, KHÔNG phải việc của panel này.
|
||||
//
|
||||
// 🔴 [K3 S164/S165 — SUPERSEDE] Câu cũ ở đây khai "KHÔNG 2 đường kết-thúc-sớm (AllowApproverFinalize /
|
||||
// CeoApprovalThreshold — anh phán «PHÁ VỠ» S155; W1 đã rào ở Designer type-10)". KHAI ĐÓ ĐÃ HẾT HIỆU LỰC:
|
||||
// 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.
|
||||
//
|
||||
// 🔴 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`.
|
||||
|
||||
@ -142,14 +142,23 @@ const LEVEL_ORDERS: LevelOrder[] = [1, 2, 3]
|
||||
|
||||
// [W1 KHKK S161] "Kế hoạch ký kết HĐ" = slot 10 của `ApprovalWorkflowApplicableType`
|
||||
// (`ApprovalWorkflow.cs:66` trước đó dừng ở `TravelRequest = 9`). Đặt TÊN cho số 10
|
||||
// vì nó dùng ở HAI nơi trong file: map deep-link ngay bên dưới + rào 2 ô "duyệt sớm"
|
||||
// trong Designer — số trần rải 2 chỗ là kiểu sửa 1 quên 1.
|
||||
// vì nó dùng ở HAI nơi trong file: map deep-link ngay bên dưới + gợi ý mã quy trình
|
||||
// mặc định — số trần rải 2 chỗ là kiểu sửa 1 quên 1.
|
||||
// [K3 S164 — GỠ KHOÁ] Chỗ dùng THỨ BA của hằng này (rào 2 ô "duyệt sớm" trong
|
||||
// Designer) ĐÃ GỠ, xem khối ghi chú ngay dưới.
|
||||
const TYPE_INT_CONTRACT_SIGNING_PLAN = 10
|
||||
|
||||
// Câu giải thích DÙNG CHUNG cho tooltip 2 ô bị khoá + dòng ghi chú (1 chuỗi, không
|
||||
// chép 3 lần rồi lệch chữ).
|
||||
const KHKK_EARLY_FINALIZE_NOTE =
|
||||
'Chưa hỗ trợ cho quy trình Kế hoạch ký kết HĐ — backend chưa xử lý đường "duyệt sớm", tick vào sẽ KHÔNG có tác dụng.'
|
||||
// [K3 S164 — SUPERSEDE W1 S161] 🔴 Duyệt sớm ĐÃ MỞ cho KHKK từ K3 (owner đảo phán
|
||||
// quyết S155 @S164: TP.CCM quyết lên CEO hay kết thúc). Trước đó file này có hằng
|
||||
// `KHKK_EARLY_FINALIZE_NOTE` + cờ `earlyFinalizeLocked` ép 2 ô "Duyệt thẳng Cấp cuối"
|
||||
// và "Duyệt là KẾT THÚC" về mờ-và-false cho type 10, vì backend chưa port đường
|
||||
// finalize. BE đã port nhánh finalize ở K3 CÙNG WAVE ⇒ cờ hết là "cờ chết", khoá lại
|
||||
// thành khoá SAI (chặn đúng cái owner vừa yêu cầu mở). Gỡ ở CẢ HAI lớp đã rào —
|
||||
// render (banner + `disabled`) và payload (`levelFlagsPayload` ép false) — vì chỉ gỡ
|
||||
// 1 lớp thì màn hình và dữ liệu nói hai đằng.
|
||||
// Banner giải thích đi kèm cũng XOÁ (không đổi chữ): nội dung nó chỉ để cảnh báo
|
||||
// khoá, hết khoá là hết việc; còn câu supersede mang từ vựng nội bộ ("K3", "S155/
|
||||
// S164") nên thuộc về comment này, không đẩy lên UI admin.
|
||||
|
||||
// FE typeCode → BE int (giống MenuKeys ApplicableType)
|
||||
const TYPE_CODE_TO_INT: Record<string, number> = {
|
||||
@ -239,12 +248,11 @@ function makeDefaultLevelEntry(order: LevelOrder, approverUserId: string): EditL
|
||||
// - 2 cờ ĐỔI-ĐƯỜNG-ĐI (SkipToFinal, Finalize): PUT chỉ ECHO — gửi lệch ⇒ 409
|
||||
// (ApprovalWorkflowV2AdminFeatures.cs:580-585). Vẫn PHẢI gửi đúng giá trị đang lưu.
|
||||
//
|
||||
// [W1 KHKK S161] `lockEarlyFinalize` = quy trình thuộc loại CHƯA hỗ trợ 2 đường
|
||||
// "duyệt sớm" (hiện chỉ Kế hoạch ký kết HĐ, type 10) ⇒ ép 2 cờ đó về `false` TẠI ĐÂY,
|
||||
// không chỉ khoá ô tick ở màn hình: khoá render mà payload vẫn bê giá trị cũ thì
|
||||
// clone/dữ liệu lạ vẫn đẩy `true` lên BE. Echo của PUT vẫn khớp DB vì mọi bản type 10
|
||||
// đều do chính màn này tạo ra với 2 cờ = false ⇒ không sinh 409.
|
||||
function levelFlagsPayload(e: EditLevelEntry, lockEarlyFinalize: boolean) {
|
||||
// [K3 S164 — GỠ KHOÁ] Tham số thứ 2 `lockEarlyFinalize` (W1 S161, ép 2 cờ "duyệt sớm"
|
||||
// về `false` cho type 10) ĐÃ BỎ: mọi loại quy trình nay gửi đúng giá trị admin tick.
|
||||
// Xoá hẳn tham số chứ không để nó nằm chết — `noUnusedParameters` bật, và param mồ côi
|
||||
// ở hàm 2 call-site là mồi cho lượt sau tưởng còn rào.
|
||||
function levelFlagsPayload(e: EditLevelEntry) {
|
||||
return {
|
||||
allowReturnOneLevel: e.allowReturnOneLevel,
|
||||
allowReturnOneStep: e.allowReturnOneStep,
|
||||
@ -252,8 +260,8 @@ function levelFlagsPayload(e: EditLevelEntry, lockEarlyFinalize: boolean) {
|
||||
allowReturnToDrafter: e.allowReturnToDrafter,
|
||||
allowApproverEditDetails: e.allowApproverEditDetails,
|
||||
allowApproverEditBudget: e.allowApproverEditBudget,
|
||||
allowApproverSkipToFinal: lockEarlyFinalize ? false : e.allowApproverSkipToFinal,
|
||||
allowApproverFinalize: lockEarlyFinalize ? false : e.allowApproverFinalize,
|
||||
allowApproverSkipToFinal: e.allowApproverSkipToFinal,
|
||||
allowApproverFinalize: e.allowApproverFinalize,
|
||||
allowApproverDelete: e.allowApproverDelete,
|
||||
}
|
||||
}
|
||||
@ -630,13 +638,10 @@ function Designer({
|
||||
const isEdit = editTarget !== null
|
||||
const source = editTarget ?? cloneFrom
|
||||
|
||||
// [W1 KHKK S161 — chặn "cờ chết"] 2 ô "duyệt sớm" (Duyệt thẳng Cấp cuối / Duyệt là
|
||||
// KẾT THÚC) ĐỔI ĐƯỜNG ĐI của phiếu. Module Kế hoạch ký kết HĐ chưa port 2 đường này
|
||||
// ở backend ⇒ để ô tick sống = admin tưởng đã bật mà phiếu vẫn chạy y như cũ (cờ
|
||||
// chết đánh lừa người cấu hình). Khoá ở CẢ HAI lớp: render (disabled + ghi chú) và
|
||||
// payload (levelFlagsPayload ép false).
|
||||
const earlyFinalizeLocked = applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN
|
||||
|
||||
// [K3 S164 — GỠ KHOÁ] `earlyFinalizeLocked` (W1 S161) ĐÃ BỎ HẲN: 2 ô "duyệt sớm"
|
||||
// nay sống cho MỌI loại quy trình, kể cả Kế hoạch ký kết HĐ (type 10) — BE đã port
|
||||
// đường finalize ở K3 cùng wave, và owner chốt TP.CCM là người quyết lên CEO hay
|
||||
// kết thúc. Lý do đầy đủ ở khối ghi chú đầu file (gần `TYPE_INT_CONTRACT_SIGNING_PLAN`).
|
||||
const initialSteps: EditStep[] = useMemo(
|
||||
() => (source ? copyFromDefinition(source) : [makeEmptyStep(1)]),
|
||||
[source],
|
||||
@ -780,7 +785,7 @@ function Designer({
|
||||
name: `Cấp ${e.order}`,
|
||||
approverUserId: e.approverUserId,
|
||||
// 9 cờ qua ĐÚNG 1 hàm dùng chung với POST — xem levelFlagsPayload().
|
||||
...levelFlagsPayload(e, earlyFinalizeLocked),
|
||||
...levelFlagsPayload(e),
|
||||
})),
|
||||
})),
|
||||
})
|
||||
@ -812,7 +817,7 @@ function Designer({
|
||||
// field (KHÔNG spread `...e`) ⇒ thiếu 1 cờ thì TS vẫn build sạch, ô tick
|
||||
// vẫn hiện, chỉ BE không bao giờ nhận. Từ khi có call-site thứ hai (PUT),
|
||||
// 9 cờ gom vào levelFlagsPayload() dùng CHUNG — thêm cờ mới chỉ sửa 1 nơi.
|
||||
...levelFlagsPayload(e, earlyFinalizeLocked),
|
||||
...levelFlagsPayload(e),
|
||||
})),
|
||||
})),
|
||||
})
|
||||
@ -1278,51 +1283,32 @@ function Designer({
|
||||
/>
|
||||
<span>Cho phép chỉnh sửa Section ngân sách lúc đang duyệt</span>
|
||||
</label>
|
||||
{/* [W1 KHKK S161] Ghi chú vì sao 2 ô ngay dưới bị mờ. Chỉ hiện
|
||||
cho loại quy trình chưa hỗ trợ "duyệt sớm" (type 10 — Kế hoạch
|
||||
ký kết HĐ); các loại khác giao diện KHÔNG đổi 1 pixel. */}
|
||||
{earlyFinalizeLocked && (
|
||||
<div className="col-span-2 rounded border border-slate-200 bg-slate-50 px-1.5 py-1 text-[10.5px] leading-relaxed text-slate-500">
|
||||
{KHKK_EARLY_FINALIZE_NOTE}
|
||||
</div>
|
||||
)}
|
||||
{/* Mig 31 (S23 t1) — F2 AllowApproverSkipToFinal admin opt-in per-slot.
|
||||
Approver tick = skip thẳng Cấp cuối khi đang ChoDuyet. */}
|
||||
<label
|
||||
className={
|
||||
earlyFinalizeLocked
|
||||
? 'col-span-2 flex cursor-not-allowed items-center gap-1 text-[11px] text-slate-400'
|
||||
: 'col-span-2 flex items-center gap-1 text-[11px] text-slate-700'
|
||||
}
|
||||
title={earlyFinalizeLocked ? KHKK_EARLY_FINALIZE_NOTE : undefined}
|
||||
>
|
||||
Approver tick = skip thẳng Cấp cuối khi đang ChoDuyet.
|
||||
[K3 S165 — gate-K3 F-2] KHKK (type-10) CHỈ mở ô "KẾT THÚC" (dưới):
|
||||
service KHKK CHƯA port nhánh SkipToFinal + BE CREATE vẫn ép-false cờ
|
||||
này cho type-10 ⇒ ô này DISABLE cho KHKK kẻo tick bị nuốt im lặng
|
||||
(class "UI nói một đằng DB một nẻo" — đúng thứ W1 S161 cảnh báo). */}
|
||||
<label className={`col-span-2 flex items-center gap-1 text-[11px] ${applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN ? 'text-slate-400' : 'text-slate-700'}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-3 w-3"
|
||||
// [W1 KHKK S161] Khoá thì hiện KHÔNG tick — khớp đúng cái
|
||||
// payload gửi lên (levelFlagsPayload ép false), không để màn
|
||||
// hình nói một đằng dữ liệu một nẻo.
|
||||
checked={earlyFinalizeLocked ? false : entry.allowApproverSkipToFinal}
|
||||
disabled={earlyFinalizeLocked}
|
||||
checked={applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN ? false : entry.allowApproverSkipToFinal}
|
||||
disabled={applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN}
|
||||
title={applicableType === TYPE_INT_CONTRACT_SIGNING_PLAN ? 'Kế hoạch ký kết chưa hỗ trợ duyệt vượt cấp — service chưa port nhánh này (K3)' : undefined}
|
||||
onChange={e => updateField('allowApproverSkipToFinal', e.target.checked)}
|
||||
/>
|
||||
<span>Cho phép duyệt thẳng Cấp cuối khi đang duyệt</span>
|
||||
</label>
|
||||
{/* [Mig 58 CCM item 4 — anh Kiệt FDC] Duyệt là KẾT THÚC: NV slot
|
||||
này duyệt xong → DaDuyet luôn, KHÔNG trình tiếp CEO (vd Cấp anh Chương). */}
|
||||
<label
|
||||
className={
|
||||
earlyFinalizeLocked
|
||||
? 'col-span-2 flex cursor-not-allowed items-center gap-1 text-[11px] text-slate-400'
|
||||
: 'col-span-2 flex items-center gap-1 text-[11px] text-emerald-700'
|
||||
}
|
||||
title={earlyFinalizeLocked ? KHKK_EARLY_FINALIZE_NOTE : undefined}
|
||||
>
|
||||
này duyệt xong → DaDuyet luôn, KHÔNG trình tiếp CEO (vd Cấp anh Chương).
|
||||
[K3 S164] Với Kế hoạch ký kết HĐ đây CHÍNH LÀ ô owner cần: tick cho
|
||||
Cấp TP.CCM (anh Chương) ⇒ anh ấy quyết kết thúc hay trình tiếp CEO. */}
|
||||
<label className="col-span-2 flex items-center gap-1 text-[11px] text-emerald-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-3 w-3"
|
||||
checked={earlyFinalizeLocked ? false : entry.allowApproverFinalize}
|
||||
disabled={earlyFinalizeLocked}
|
||||
checked={entry.allowApproverFinalize}
|
||||
onChange={e => updateField('allowApproverFinalize', e.target.checked)}
|
||||
/>
|
||||
<span>Duyệt là KẾT THÚC quy trình (không trình tiếp CEO)</span>
|
||||
|
||||
Reference in New Issue
Block a user