From 7d39d8d61bebdb681f816b8c9618cd9d2e47d8dd Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 23 Jul 2026 17:11:36 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20PurchaseEvaluation:=20cho=20ng=C6=B0?= =?UTF-8?q?=E1=BB=9Di=20so=E1=BA=A1n=20x=C3=B3a/t=E1=BA=A3i=20l=E1=BA=A1i?= =?UTF-8?q?=20file=20b=E1=BA=A3ng=20so=20s=C3=A1nh=20khi=20phi=E1=BA=BFu?= =?UTF-8?q?=20b=E1=BB=8B=20Tr=E1=BA=A3=20l=E1=BA=A1i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nút đính kèm 'Bảng so sánh giá' (GeneralAttachmentsSection) trước gate theo readOnly MÀN HÌNH: màn Danh sách mở phiếu readOnly=true nên ẩn nút xóa/tải ở MỌI phase, kể cả Trả lại (TraLai=98) vốn đã nằm trong isEditablePhase. Đổi gate sang (isAdmin || isDrafter) && isEditablePhase(phase), chỉ tách riêng khối đính kèm — HoSoLink + winner giữ theo readOnly màn hình (không mở nhầm); SupplierAttachmentsCell giữ nguyên carve-out approverEditMode (Mig 28 F3); BE không thêm guard (giữ S78 approver-upload). Mirror fe-user + fe-admin. tsc -b + vite build sạch 2 app. Co-Authored-By: Claude Opus 4.8 --- fe-admin/src/components/pe/PeDetailTabs.tsx | 13 ++++++++++++- fe-user/src/components/pe/PeDetailTabs.tsx | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fe-admin/src/components/pe/PeDetailTabs.tsx b/fe-admin/src/components/pe/PeDetailTabs.tsx index d28eaa1..a5e6043 100644 --- a/fe-admin/src/components/pe/PeDetailTabs.tsx +++ b/fe-admin/src/components/pe/PeDetailTabs.tsx @@ -1977,6 +1977,17 @@ function ThongTinChonThauSection({ ev, readOnly = false }: { ev: PeDetailBundle; a => a.purchaseEvaluationSupplierId === null && a.purpose !== PeAttachmentPurpose.ApprovalAttachment, ) + // [Fix UAT — phiếu bị Trả lại: không xóa/tải lại được file "Bảng so sánh"] + // Nút xóa/tải file so sánh phải theo NGƯỜI-SOẠN (hoặc Admin) + TRẠNG-THÁI-được-sửa, + // KHÔNG theo readOnly của MÀN HÌNH: màn "Danh sách" mở phiếu readOnly=true nên ẩn + // nút ở MỌI phase — kể cả Trả lại, vốn nằm trong isEditablePhase. Chỉ tách RIÊNG + // khối đính kèm này; HoSoLink + winner bên dưới vẫn theo readOnly màn hình (không + // mở nhầm). Chỉ áp cho "Bảng so sánh" (GeneralAttachmentsSection) — file per-NCC + // (SupplierAttachmentsCell) giữ nguyên carve-out approverEditMode (Mig 28 F3). + const { user: currentUser } = useAuth() + const isAdmin = currentUser?.roles?.includes('Admin') ?? false + const isDrafter = currentUser?.id != null && ev.drafterUserId === currentUser.id + const attachEditable = (isAdmin || isDrafter) && isEditablePhase(ev.phase) return (
@@ -2010,7 +2021,7 @@ function ThongTinChonThauSection({ ev, readOnly = false }: { ev: PeDetailBundle; {/* 4. Bảng so sánh giá */}
4. Bảng so sánh giá
- +
{/* 5. Link hồ sơ */} diff --git a/fe-user/src/components/pe/PeDetailTabs.tsx b/fe-user/src/components/pe/PeDetailTabs.tsx index d28eaa1..a5e6043 100644 --- a/fe-user/src/components/pe/PeDetailTabs.tsx +++ b/fe-user/src/components/pe/PeDetailTabs.tsx @@ -1977,6 +1977,17 @@ function ThongTinChonThauSection({ ev, readOnly = false }: { ev: PeDetailBundle; a => a.purchaseEvaluationSupplierId === null && a.purpose !== PeAttachmentPurpose.ApprovalAttachment, ) + // [Fix UAT — phiếu bị Trả lại: không xóa/tải lại được file "Bảng so sánh"] + // Nút xóa/tải file so sánh phải theo NGƯỜI-SOẠN (hoặc Admin) + TRẠNG-THÁI-được-sửa, + // KHÔNG theo readOnly của MÀN HÌNH: màn "Danh sách" mở phiếu readOnly=true nên ẩn + // nút ở MỌI phase — kể cả Trả lại, vốn nằm trong isEditablePhase. Chỉ tách RIÊNG + // khối đính kèm này; HoSoLink + winner bên dưới vẫn theo readOnly màn hình (không + // mở nhầm). Chỉ áp cho "Bảng so sánh" (GeneralAttachmentsSection) — file per-NCC + // (SupplierAttachmentsCell) giữ nguyên carve-out approverEditMode (Mig 28 F3). + const { user: currentUser } = useAuth() + const isAdmin = currentUser?.roles?.includes('Admin') ?? false + const isDrafter = currentUser?.id != null && ev.drafterUserId === currentUser.id + const attachEditable = (isAdmin || isDrafter) && isEditablePhase(ev.phase) return (
@@ -2010,7 +2021,7 @@ function ThongTinChonThauSection({ ev, readOnly = false }: { ev: PeDetailBundle; {/* 4. Bảng so sánh giá */}
4. Bảng so sánh giá
- +
{/* 5. Link hồ sơ */}