[CLAUDE] Contract: F-1 o-tich opt-in level-finalize man duyet KHKK (DTO expose co + UI x2 app, khuon PE-live S97)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m21s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m21s
Gate-K3 F-1 MAJOR: BE ApplyLevelFinalize ?? true ma 0 UI gui false => tram CEO chi di duoc bang admin bo co Designer. Va: expose AllowApproverFinalize vao ContractSigningPlanWorkflowLevelDto (1 site :640) + panel KHKK x2 app suy actingLevel mirror ResolveActingLevel (own-truoc, Admin fallback First) + o-tich OPT-IN khuon PE-live S97 (G-1 reviewer: default false — fail-mode an toan len CEO; ratify treo so CHO-ANH) + body 3-field chi khi approve. Reviewer PASS-WITH-FLAGS 7 (sub-reviewer-f1-checkbox.md): G-1 flip ap, G-5 bo "(CEO)" ap, G-6 tach commit ap (K4a rieng), G-2/G-3/G-4 no nhip-K4, G-7 vao commit K4a. Suite 614/0 + filter KHKK 34/34 + npm x2 + SHA-pair b37cd292. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -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<KhkkTransitionActionValue | null>(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<KhkkTransitionResult>(
|
||||
`/contract-signing-plans/${plan.id}/transitions`,
|
||||
body,
|
||||
@ -295,7 +320,15 @@ export function KhkkWorkflowPanel({
|
||||
<button
|
||||
type="button"
|
||||
disabled={blockedByLevel}
|
||||
onClick={() => { if (!blockedByLevel) { setComment(''); setAction(KhkkTransitionAction.Approve) } }}
|
||||
onClick={() => {
|
||||
if (!blockedByLevel) {
|
||||
setComment('')
|
||||
// [F-1 S166 · G-1] reset về default KHÔNG-tick (opt-IN, khuôn PE S97) mỗi
|
||||
// lần mở dialog — lựa chọn tích là per-lượt-duyệt, không dính lần trước.
|
||||
setApplyLevelFinalize(false)
|
||||
setAction(KhkkTransitionAction.Approve)
|
||||
}
|
||||
}}
|
||||
title={blockedByLevel ? 'Cấp hiện tại chỉ người được phân công mới thao tác được.' : 'Duyệt cấp hiện tại'}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 rounded border px-2 py-1 text-[11px] font-bold transition',
|
||||
@ -408,6 +441,33 @@ export function KhkkWorkflowPanel({
|
||||
⚠ Kế hoạch bị khoá hoàn toàn (không sửa / không duyệt tiếp). Người soạn phải lập kế hoạch mới.
|
||||
</div>
|
||||
)}
|
||||
{/* [F-1 S166 · G-1/G-5 reviewer] Cấp cấu hình KẾT THÚC (AllowApproverFinalize) = ô-tích
|
||||
OPT-IN, khuôn PE-LIVE S97 (`PeWorkflowPanel.tsx:61` useState(false) + JSX `:772-791`;
|
||||
comment "opt-out S96" ngay trên khối JSX đó là chú-thích CHẾT — G-1 bắt đúng bẫy này):
|
||||
default KHÔNG tick = duyệt xong TRÌNH TIẾP cấp sau; TÍCH = gửi applyLevelFinalize=true
|
||||
⇒ BE kết thúc tại cấp. Không viết "(CEO)" — cấp-sau là gì tuỳ cấu hình quy trình (G-5).
|
||||
KHÁC PE: không nhắc "chọn giá chốt" — KHKK chốt giá per-dòng do BE tự làm ở finalize
|
||||
choke-point (`ApplyApprovedValuesOnFinalize`), panel không có price-picker. */}
|
||||
{action === KhkkTransitionAction.Approve && approverFinalizeEligible && (
|
||||
<div className="mb-3 rounded border border-emerald-300 bg-emerald-50 px-3 py-2 text-[12px] text-emerald-800">
|
||||
<label className="flex cursor-pointer items-start gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-0.5"
|
||||
checked={applyLevelFinalize}
|
||||
onChange={e => setApplyLevelFinalize(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
<span className="font-medium">Cấp này KẾT THÚC quy trình</span>
|
||||
<span className="mt-0.5 block text-[11px] text-emerald-700/80">
|
||||
{applyLevelFinalize
|
||||
? 'Đã tích — duyệt xong là kế hoạch "Đã duyệt", KẾT THÚC tại cấp này, KHÔNG trình tiếp cấp sau.'
|
||||
: 'Mặc định: duyệt xong kế hoạch TRÌNH TIẾP lên cấp sau của quy trình. TÍCH vào nếu muốn KẾT THÚC ngay tại cấp này.'}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<Label>
|
||||
Ý kiến{commentRequired ? <span className="text-red-500"> *</span> : ' (tuỳ chọn)'}
|
||||
</Label>
|
||||
|
||||
@ -231,6 +231,9 @@ export interface KhkkWorkflowLevelDto {
|
||||
name: string | null
|
||||
approverUserId: string
|
||||
approverFullName: string | null
|
||||
// [F-1 S166] Cờ per-Cấp "được KẾT THÚC quy trình tại cấp này" (admin tick ở Designer).
|
||||
// Panel dùng để hiện ô-tích opt-out khi duyệt — mirror BE `ResolveActingLevel`.
|
||||
allowApproverFinalize: boolean
|
||||
}
|
||||
|
||||
// `ContractSigningPlanWorkflowStepDto:112-117` — chỉ có `departmentId`, KHÔNG có tên phòng.
|
||||
@ -331,6 +334,10 @@ export type KhkkTransitionActionValue =
|
||||
export interface KhkkTransitionInput {
|
||||
action: KhkkTransitionActionValue
|
||||
comment: string | null
|
||||
// [F-1 S166] Opt-out level-finalize (khuôn PE S96): chỉ gửi khi action=approve.
|
||||
// BE `ContractSigningPlanTransitionBody.ApplyLevelFinalize` là `bool? = null` — absent/true
|
||||
// = auto kết-thúc-tại-cấp khi Cấp có cờ; false = người duyệt BỎ tick ⇒ trình tiếp CEO.
|
||||
applyLevelFinalize?: boolean
|
||||
}
|
||||
|
||||
// Response 200 — 3 field, ĐÚNG tên hợp-đồng. `phase` là INT (mirror `KhkkPhase`).
|
||||
|
||||
@ -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<KhkkTransitionActionValue | null>(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<KhkkTransitionResult>(
|
||||
`/contract-signing-plans/${plan.id}/transitions`,
|
||||
body,
|
||||
@ -295,7 +320,15 @@ export function KhkkWorkflowPanel({
|
||||
<button
|
||||
type="button"
|
||||
disabled={blockedByLevel}
|
||||
onClick={() => { if (!blockedByLevel) { setComment(''); setAction(KhkkTransitionAction.Approve) } }}
|
||||
onClick={() => {
|
||||
if (!blockedByLevel) {
|
||||
setComment('')
|
||||
// [F-1 S166 · G-1] reset về default KHÔNG-tick (opt-IN, khuôn PE S97) mỗi
|
||||
// lần mở dialog — lựa chọn tích là per-lượt-duyệt, không dính lần trước.
|
||||
setApplyLevelFinalize(false)
|
||||
setAction(KhkkTransitionAction.Approve)
|
||||
}
|
||||
}}
|
||||
title={blockedByLevel ? 'Cấp hiện tại chỉ người được phân công mới thao tác được.' : 'Duyệt cấp hiện tại'}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 rounded border px-2 py-1 text-[11px] font-bold transition',
|
||||
@ -408,6 +441,33 @@ export function KhkkWorkflowPanel({
|
||||
⚠ Kế hoạch bị khoá hoàn toàn (không sửa / không duyệt tiếp). Người soạn phải lập kế hoạch mới.
|
||||
</div>
|
||||
)}
|
||||
{/* [F-1 S166 · G-1/G-5 reviewer] Cấp cấu hình KẾT THÚC (AllowApproverFinalize) = ô-tích
|
||||
OPT-IN, khuôn PE-LIVE S97 (`PeWorkflowPanel.tsx:61` useState(false) + JSX `:772-791`;
|
||||
comment "opt-out S96" ngay trên khối JSX đó là chú-thích CHẾT — G-1 bắt đúng bẫy này):
|
||||
default KHÔNG tick = duyệt xong TRÌNH TIẾP cấp sau; TÍCH = gửi applyLevelFinalize=true
|
||||
⇒ BE kết thúc tại cấp. Không viết "(CEO)" — cấp-sau là gì tuỳ cấu hình quy trình (G-5).
|
||||
KHÁC PE: không nhắc "chọn giá chốt" — KHKK chốt giá per-dòng do BE tự làm ở finalize
|
||||
choke-point (`ApplyApprovedValuesOnFinalize`), panel không có price-picker. */}
|
||||
{action === KhkkTransitionAction.Approve && approverFinalizeEligible && (
|
||||
<div className="mb-3 rounded border border-emerald-300 bg-emerald-50 px-3 py-2 text-[12px] text-emerald-800">
|
||||
<label className="flex cursor-pointer items-start gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-0.5"
|
||||
checked={applyLevelFinalize}
|
||||
onChange={e => setApplyLevelFinalize(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
<span className="font-medium">Cấp này KẾT THÚC quy trình</span>
|
||||
<span className="mt-0.5 block text-[11px] text-emerald-700/80">
|
||||
{applyLevelFinalize
|
||||
? 'Đã tích — duyệt xong là kế hoạch "Đã duyệt", KẾT THÚC tại cấp này, KHÔNG trình tiếp cấp sau.'
|
||||
: 'Mặc định: duyệt xong kế hoạch TRÌNH TIẾP lên cấp sau của quy trình. TÍCH vào nếu muốn KẾT THÚC ngay tại cấp này.'}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<Label>
|
||||
Ý kiến{commentRequired ? <span className="text-red-500"> *</span> : ' (tuỳ chọn)'}
|
||||
</Label>
|
||||
|
||||
@ -231,6 +231,9 @@ export interface KhkkWorkflowLevelDto {
|
||||
name: string | null
|
||||
approverUserId: string
|
||||
approverFullName: string | null
|
||||
// [F-1 S166] Cờ per-Cấp "được KẾT THÚC quy trình tại cấp này" (admin tick ở Designer).
|
||||
// Panel dùng để hiện ô-tích opt-out khi duyệt — mirror BE `ResolveActingLevel`.
|
||||
allowApproverFinalize: boolean
|
||||
}
|
||||
|
||||
// `ContractSigningPlanWorkflowStepDto:112-117` — chỉ có `departmentId`, KHÔNG có tên phòng.
|
||||
@ -331,6 +334,10 @@ export type KhkkTransitionActionValue =
|
||||
export interface KhkkTransitionInput {
|
||||
action: KhkkTransitionActionValue
|
||||
comment: string | null
|
||||
// [F-1 S166] Opt-out level-finalize (khuôn PE S96): chỉ gửi khi action=approve.
|
||||
// BE `ContractSigningPlanTransitionBody.ApplyLevelFinalize` là `bool? = null` — absent/true
|
||||
// = auto kết-thúc-tại-cấp khi Cấp có cờ; false = người duyệt BỎ tick ⇒ trình tiếp CEO.
|
||||
applyLevelFinalize?: boolean
|
||||
}
|
||||
|
||||
// Response 200 — 3 field, ĐÚNG tên hợp-đồng. `phase` là INT (mirror `KhkkPhase`).
|
||||
|
||||
@ -118,7 +118,11 @@ public record ContractSigningPlanWorkflowLevelDto(
|
||||
int Order,
|
||||
string? Name,
|
||||
Guid ApproverUserId,
|
||||
string? ApproverFullName);
|
||||
string? ApproverFullName,
|
||||
// [F-1 S166] Cờ per-Cấp cho ô-tích opt-out "Cấp này KẾT THÚC" ở panel duyệt (khuôn PE S96).
|
||||
// FE mirror `ResolveActingLevel` (own-level trước, Admin fallback level đầu nhóm) để quyết
|
||||
// hiện ô-tích — thiếu cờ này thì `applyLevelFinalize` của body transition không có UI nào gửi false.
|
||||
bool AllowApproverFinalize);
|
||||
|
||||
public record ContractSigningPlanWorkflowStepDto(
|
||||
Guid Id,
|
||||
@ -635,7 +639,8 @@ public class GetContractSigningPlanQueryHandler(IApplicationDbContext db, ICurre
|
||||
s.Id, s.Order, s.Name, s.DepartmentId,
|
||||
s.Levels.OrderBy(l => l.Order).Select(l => new ContractSigningPlanWorkflowLevelDto(
|
||||
l.Id, l.Order, l.Name, l.ApproverUserId,
|
||||
approverNames.TryGetValue(l.ApproverUserId, out var fn) ? fn : null)).ToList()));
|
||||
approverNames.TryGetValue(l.ApproverUserId, out var fn) ? fn : null,
|
||||
l.AllowApproverFinalize)).ToList()));
|
||||
|
||||
foreach (var l in s.Levels)
|
||||
levelMeta[l.Id] = (s.Order, s.Name, l.Order, l.ApproverUserId);
|
||||
|
||||
Reference in New Issue
Block a user