From 9e63e2da1015576ff08f5e45cb4fdd7a5726e51c Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Fri, 8 May 2026 15:18:22 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20PE:=20V2-aware=20Inbox/List=20+=202?= =?UTF-8?q?=20dropdown=20filter=20quy=20tr=C3=ACnh=20+=20tr=E1=BA=A1ng=20t?= =?UTF-8?q?h=C3=A1i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User báo: "Phiếu chưa thấy lên trong danh sách duyệt — chắc do chưa ăn vào flow. Tách thành 2 cái dropdown là list quy trình duyệt và list trạng thái. Debug trước, phân quyền rút gọn lại sau." BE — V2-aware permission + filter (Application/PurchaseEvaluations/ PurchaseEvaluationFeatures.cs): - ListPurchaseEvaluationsQuery +ApprovalWorkflowId? Guid? param + IDOR loose: phiếu pin V2 → mọi authenticated user thấy được (UAT) - GetMyPurchaseEvaluationInbox V2-aware: ResolveV2InboxIdsAsync helper precompute Set phiếu Phase=ChoDuyet pin V2 + actor ∈ Cấp hiện tại approvers (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder match Step.Order + Level.Order). Inbox where = eligiblePhases.Contains || v2InboxIds.Contains. eligiblePhases admin +ChoDuyet. - GetById Detail loose: V2 pin → cho non-Drafter xem (skip eligiblePhases check). API Controller: - PurchaseEvaluationsController.List +approvalWorkflowId query param FE — 2 dropdown filter (cả 2 app mirror): - PurchaseEvaluationsListPage: +URL param `awId` filter quy trình - useQuery `approval-workflows-v2-filter` load list V2 active+history theo applicableType=typeFilter (chỉ enabled khi có type) - Render Select riêng "Tất cả quy trình duyệt" (chỉ show !pendingMe vì Inbox dùng API endpoint khác) + Select "Tất cả trạng thái" giữ - Display option: "QT-DN-V2-001 v01 — Tên quy trình" Verify: BE build 0 error · 2 FE builds OK. Test luồng eoffice: 1. Drafter trình phiếu V2 → Phase=ChoDuyet 2. Login NV X (approver Cấp 1) vào "Duyệt NCC > Duyệt" (?pendingMe=1) → phiếu hiện trong list 3. Login NV Y (không phải approver) → list rỗng (đúng spec) 4. Vào "Duyệt NCC > Danh sách" (không pendingMe) → 2 dropdown: - Quy trình duyệt: filter theo workflow specific - Trạng thái: filter theo Phase --- .../pages/pe/PurchaseEvaluationsListPage.tsx | 29 +++++++- .../pages/pe/PurchaseEvaluationsListPage.tsx | 29 +++++++- .../PurchaseEvaluationsController.cs | 3 +- .../PurchaseEvaluationFeatures.cs | 68 +++++++++++++++++-- 4 files changed, 120 insertions(+), 9 deletions(-) diff --git a/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx b/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx index 76e1bbe..57a9147 100644 --- a/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx +++ b/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx @@ -33,10 +33,25 @@ export function PurchaseEvaluationsListPage() { const pendingMe = sp.get('pendingMe') === '1' const search = sp.get('q') ?? '' const phase = sp.get('phase') ?? '' + const approvalWorkflowId = sp.get('awId') ?? '' // Mig 23 — filter quy trình const selectedId = sp.get('id') + // Mig 23 — list quy trình duyệt V2 cho dropdown filter (filter theo type screen) + const approvalWorkflows = useQuery({ + queryKey: ['approval-workflows-v2-filter', typeFilter], + queryFn: async () => { + if (!typeFilter) return [] + const res = await api.get<{ types: { applicableType: number; history: { id: string; code: string; version: number; name: string; isActive: boolean }[] }[] }>( + '/approval-workflows-v2', + { params: { applicableType: typeFilter } }, + ) + return res.data.types.find(t => t.applicableType === typeFilter)?.history ?? [] + }, + enabled: !!typeFilter, + }) + const list = useQuery({ - queryKey: ['pe-list', { typeFilter, pendingMe, search, phase }], + queryKey: ['pe-list', { typeFilter, pendingMe, search, phase, approvalWorkflowId }], queryFn: async () => { if (pendingMe) { const res = await api.get('/purchase-evaluations/inbox', { @@ -50,6 +65,7 @@ export function PurchaseEvaluationsListPage() { search: search || undefined, type: typeFilter ?? undefined, phase: phase || undefined, + approvalWorkflowId: approvalWorkflowId || undefined, }, }) return res.data @@ -119,6 +135,17 @@ export function PurchaseEvaluationsListPage() { className="pl-8" /> + {/* Mig 23 — 2 dropdown tách: Quy trình duyệt + Trạng thái */} + {!pendingMe && ( + + )} setParam('awId', e.target.value)}> + + {approvalWorkflows.data?.map(w => ( + + ))} + + )}